Option Explicit

Private Const TEST_TABLE_NAME As String = "table1"
Private Const TEST_POINT As String = "."
Private Const TEST_COL_NAME As String = "col1"
Private Const TEST_SHARE_LINE_COMMENT As String = "--aaaaaa"
Private Const TEST_SINGLE_LINE_COMMENT As String = "--bbbbb"

' ======================================================================
' 执行所有测试用例
' 这里只有格式化器的解析器中的 sql 列对象的解析方法的测试
' ======================================================================
Public Sub test_all_case()
    ' 测试: 匹配Exists表达式
    test_parseExistsExpr

    ' 匹配真实列或全数据列
    test_parseColFromStrName

    ' 测试: 匹配任意形式的列表达式
    test_parseMixedColExpr

    ' 测试: 匹配一个单独的列对象
    test_parseSingleCol

    ' 测试: 从运算符开始匹配表达式
    test_parseExprColStartWithOperator

    ' 测试: 匹配单列名称表达式
    test_parseColExpr

    ' 测试: 匹配函数列对象
    test_parseFunction

    ' 测试: 匹配括号包裹的多个列对象
    test_parseParenthesisWrapperMultiCol

    ' 测试: 匹配 case 表达式
    test_parseCaseStatement

    ' 测试： 匹配case中的when表达式
    test_parseCaseWhenStatement

    ' 测试: 匹配case中的else表达式
    test_parseCaseElseStatement

    ' 测试: 处理 cast 函数
    test_parseCastFunc

    ' 测试: 匹配cast函数的函数体
    test_parseCastFuncBody

End Sub

' ======================================================================
' 测试通用处理
' ======================================================================

' 创建一个格式化器的 sql 解析器
Private Function createSqlFormatterAnalyzer() As SqlFormatterAnalyzer
    Dim config As SqlFormatterConfiguration
    Set config = New SqlFormatterConfiguration

    Dim result As SqlFormatterAnalyzer
    Set result = New SqlFormatterAnalyzer

    result.Init config

    Set createSqlFormatterAnalyzer = result
End Function

' 创建一个扫描器
Private Function getObjArrayRefScanner(ParamArray tokens() As Variant) As ObjArrayRefScanner
    Dim i As Integer
    Dim t As SqlToken

    Dim tokenCount As Integer
    tokenCount = UBound(tokens)

    ' 创建一个和参数数量相同的数组引用
    Dim meanTokens As IObjArrayRef
    Set meanTokens = UtilsArrayRef.createRealObjArrayRef(tokenCount)

    ' 遍历参数，并保持到数组引用
    For i = 0 To UBound(tokens)
        Set t = tokens(i)

        meanTokens.Add t
    Next

    ' 创建扫描器
    Dim scanner As ObjArrayRefScanner
    Set scanner = New ObjArrayRefScanner
    scanner.Init meanTokens

    ' return scanner
    Set getObjArrayRefScanner = scanner
End Function

' 获取一个 recursive 元素
Private Function getRecursiveToken() As SqlToken
    Set getRecursiveToken = New SqlToken
    getRecursiveToken.Init "recursive", SqlTokenType.KEY_RECURSIVE
End Function

' 获取一个 cast 元素
Private Function getCastToken() As SqlToken
    Set getCastToken = New SqlToken
    getCastToken.Init "cast", SqlTokenType.KEY_CAST
End Function

' 创建一个字符串 token
Private Function createStrToken(ByVal v As String) As SqlToken
    Set createStrToken = New SqlToken
    createStrToken.Init "'" & v & "'", SqlTokenType.STR_STR
End Function

' where 关键字
Private Function getWhereToken() As SqlToken
    Set getWhereToken = New SqlToken
    getWhereToken.Init "where", SqlTokenType.KEY_WHERE
End Function

' case 运算符
Private Function getCaseToken() As SqlToken
    Set getCaseToken = New SqlToken
    getCaseToken.Init "case", SqlTokenType.KEY_CASE
End Function

' when 运算符
Private Function getWhenToken() As SqlToken
    Set getWhenToken = New SqlToken
    getWhenToken.Init "when", SqlTokenType.KEY_WHEN
End Function

' then 运算符
Private Function getThenToken() As SqlToken
    Set getThenToken = New SqlToken
    getThenToken.Init "then", SqlTokenType.KEY_THEN
End Function

' else 运算符
Private Function getElseToken() As SqlToken
    Set getElseToken = New SqlToken
    getElseToken.Init "else", SqlTokenType.KEY_ELSE
End Function

' end 运算符
Private Function getEndToken() As SqlToken
    Set getEndToken = New SqlToken
    getEndToken.Init "end", SqlTokenType.KEY_END
End Function

' ASC 运算符
Private Function getAscToken() As SqlToken
    Set getAscToken = New SqlToken
    getAscToken.Init "asc", SqlTokenType.KEY_ASC
End Function

' DESC 运算符
Private Function getDescToken() As SqlToken
    Set getDescToken = New SqlToken
    getDescToken.Init "desc", SqlTokenType.KEY_DESC
End Function

' || 运算符
Private Function getStrConcatToken() As SqlToken
    Set getStrConcatToken = New SqlToken
    getStrConcatToken.Init "||", SqlTokenType.OPERA_STR_CONCAT
End Function

' + 运算符
Private Function getPlusToken() As SqlToken
    Set getPlusToken = New SqlToken
    getPlusToken.Init "+", SqlTokenType.OPERA_PLUS
End Function

' - 运算符
Private Function getMinusToken() As SqlToken
    Set getMinusToken = New SqlToken
    getMinusToken.Init "-", SqlTokenType.OPERA_MINUS
End Function

' 创建一个数值元素
Private Function createNumToken(ByVal v As String) As SqlToken
    Set createNumToken = New SqlToken
    createNumToken.Init v, SqlTokenType.STR_NUM
End Function

' 获取一个 true 元素
Private Function getTrueToken() As SqlToken
    Set getTrueToken = New SqlToken
    getTrueToken.Init "true", SqlTokenType.KEY_TRUE
End Function

' 获取一个 false 元素
Private Function getFalseToken() As SqlToken
    Set getFalseToken = New SqlToken
    getFalseToken.Init "false", SqlTokenType.KEY_FALSE
End Function

' 获取一个 null 元素
Private Function getNullToken() As SqlToken
    Set getNullToken = New SqlToken
    getNullToken.Init "null", SqlTokenType.KEY_NULL
End Function

' 获取 * 元素
Private Function getAsteriskToken() As SqlToken
    Set getAsteriskToken = New SqlToken
    getAsteriskToken.Init "*", SqlTokenType.ASTERISK
End Function

' 获取 binary 关键字
Private Function getBinaryToken() As SqlToken
    Set getBinaryToken = New SqlToken
    getBinaryToken.Init "binary", SqlTokenType.KEY_BINARY
End Function

' 获取 order by 关键字
Private Function getOrderByToken() As SqlToken
    Set getOrderByToken = New SqlToken
    getOrderByToken.Init "order by", SqlTokenType.KEY_ORDER_BY
End Function

' 获取 group by 关键字
Private Function getGroupByToken() As SqlToken
    Set getGroupByToken = New SqlToken
    getGroupByToken.Init "group by", SqlTokenType.KEY_GROUP_BY
End Function

' 获取 with 关键字
Private Function getWithToken() As SqlToken
    Set getWithToken = New SqlToken
    getWithToken.Init "with", SqlTokenType.KEY_WITH
End Function

' 获取 ROLLUP 关键字
Private Function getRollupToken() As SqlToken
    Set getRollupToken = New SqlToken
    getRollupToken.Init "rollup", SqlTokenType.KEY_ROLLUP
End Function

' 获取 offset 关键字
Private Function getOffsetToken() As SqlToken
    Set getOffsetToken = New SqlToken
    getOffsetToken.Init "offset", SqlTokenType.KEY_OFFSET
End Function

' 获取 limit 关键字
Private Function getLimitToken() As SqlToken
    Set getLimitToken = New SqlToken
    getLimitToken.Init "limit", SqlTokenType.KEY_LIMIT
End Function

' 获取 is 关键字
Private Function getIsToken() As SqlToken
    Set getIsToken = New SqlToken
    getIsToken.Init "is", SqlTokenType.KEY_IS
End Function

' 获取 not 关键字
Private Function getNotToken() As SqlToken
    Set getNotToken = New SqlToken
    getNotToken.Init "not", SqlTokenType.KEY_NOT
End Function

' 获取 union 关键字
Private Function getUnionToken() As SqlToken
    Set getUnionToken = New SqlToken
    getUnionToken.Init "union", SqlTokenType.KEY_UNION
End Function

' 获取 all 关键字
Private Function getAllToken() As SqlToken
    Set getAllToken = New SqlToken
    getAllToken.Init "all", SqlTokenType.KEY_ALL
End Function

' 获取 join 关键字
Private Function getJoinToken() As SqlToken
    Set getJoinToken = New SqlToken
    getJoinToken.Init "join", SqlTokenType.KEY_JOIN
End Function

' 获取 left 关键字
Private Function getLeftToken() As SqlToken
    Set getLeftToken = New SqlToken
    getLeftToken.Init "left", SqlTokenType.KEY_LEFT
End Function

' 获取 right 关键字
Private Function getRightToken() As SqlToken
    Set getRightToken = New SqlToken
    getRightToken.Init "right", SqlTokenType.KEY_RIGHT
End Function

' 获取 inner 关键字
Private Function getInnerToken() As SqlToken
    Set getInnerToken = New SqlToken
    getInnerToken.Init "inner", SqlTokenType.KEY_INNER
End Function

' 获取 cross 关键字
Private Function getCrossToken() As SqlToken
    Set getCrossToken = New SqlToken
    getCrossToken.Init "cross", SqlTokenType.KEY_CROSS
End Function

' 获取 out 关键字
Private Function getOuterToken() As SqlToken
    Set getOuterToken = New SqlToken
    getOuterToken.Init "outer", SqlTokenType.KEY_OUTER
End Function

' 获取 use 关键字
Private Function getUseToken() As SqlToken
    Set getUseToken = New SqlToken
    getUseToken.Init "use", SqlTokenType.KEY_USE
End Function

' 获取 index 关键字
Private Function getIndexToken() As SqlToken
    Set getIndexToken = New SqlToken
    getIndexToken.Init "index", SqlTokenType.KEY_INDEX
End Function

' 获取 ignore 关键字
Private Function getIgnoreToken() As SqlToken
    Set getIgnoreToken = New SqlToken
    getIgnoreToken.Init "ignore", SqlTokenType.KEY_IGNORE
End Function

' 获取 key 关键字
Private Function getKeyToken() As SqlToken
    Set getKeyToken = New SqlToken
    getKeyToken.Init "key", SqlTokenType.KEY_KEY
End Function

' 获取 for 关键字
Private Function getForToken() As SqlToken
    Set getForToken = New SqlToken
    getForToken.Init "for", SqlTokenType.KEY_FOR
End Function

' 获取 force 关键字
Private Function getForceToken() As SqlToken
    Set getForceToken = New SqlToken
    getForceToken.Init "force", SqlTokenType.KEY_FORCE
End Function

' 获取 as 关键字
Private Function getAsToken() As SqlToken
    Set getAsToken = New SqlToken
    getAsToken.Init "as", SqlTokenType.KEY_AS
End Function

' 获取左括号
Private Function getLeftParenthesisToken() As SqlToken
    Set getLeftParenthesisToken = New SqlToken
    getLeftParenthesisToken.Init "(", SqlTokenType.LEFT_PARENTHESIS
End Function

' 获取右括号
Private Function getRightParenthesisToken() As SqlToken
    Set getRightParenthesisToken = New SqlToken
    getRightParenthesisToken.Init ")", SqlTokenType.RIGHT_PARENTHESIS
End Function

' 获取 partition 关键字
Private Function getPartitionToken() As SqlToken
    Set getPartitionToken = New SqlToken
    getPartitionToken.Init "partition", SqlTokenType.KEY_PARTITION
End Function

' 获取逻辑运算符: and
Private Function getAndToken() As SqlToken
    Set getAndToken = New SqlToken
    getAndToken.Init "and", SqlTokenType.KEY_AND
End Function

' 获取逻辑运算符: or
Private Function getOrToken() As SqlToken
    Set getOrToken = New SqlToken
    getOrToken.Init "or", SqlTokenType.KEY_OR
End Function

' 获取一个条件运算符: <=
Private Function getLeConditionOperator() As SqlToken
    Set getLeConditionOperator = New SqlToken
    getLeConditionOperator.Init "<=", SqlTokenType.OPERA_LE
End Function

' 获取一个任意名称的 STR_NAME token
Private Function getStrNameToken(ByRef s As String) As SqlToken
    Set getStrNameToken = New SqlToken
    getStrNameToken.Init s, SqlTokenType.STR_NAME
End Function

' 获取表名 token
Private Function getTableNameToken() As SqlToken
    Set getTableNameToken = New SqlToken
    getTableNameToken.Init TEST_TABLE_NAME, SqlTokenType.STR_NAME
End Function

' 获取 [.] token
Private Function getPointToken() As SqlToken
    Set getPointToken = New SqlToken
    getPointToken.Init TEST_POINT, SqlTokenType.POINT
End Function

' 获取列名 token
Private Function getColNameToken() As SqlToken
    Set getColNameToken = New SqlToken
    getColNameToken.Init TEST_COL_NAME, SqlTokenType.STR_NAME
End Function

' 创建和前一个元素在同一行的注释
Private Function createShareLineCommentToken(ByRef v As String) As SqlToken
    Set createShareLineCommentToken = New SqlToken
    createShareLineCommentToken.Init v, SqlTokenType.SHARE_LINE_COMMENT
End Function

' 获取一个单行的注释
Private Function createSingleLineCommentToken(ByRef v As String) As SqlToken
    Set createSingleLineCommentToken = New SqlToken
    createSingleLineCommentToken.Init v, SqlTokenType.SINGLE_LINE_COMMENT
End Function

' 获取和前一个元素在同一行的注释
Private Function getShareLineCommentToken() As SqlToken
    Set getShareLineCommentToken = New SqlToken
    getShareLineCommentToken.Init TEST_SHARE_LINE_COMMENT, SqlTokenType.SHARE_LINE_COMMENT
End Function

' 获取一个单行的注释
Private Function getSingleLineCommentToken() As SqlToken
    Set getSingleLineCommentToken = New SqlToken
    getSingleLineCommentToken.Init TEST_SINGLE_LINE_COMMENT, SqlTokenType.SINGLE_LINE_COMMENT
End Function

' 获取一个逗号的元素
Private Function getCommaToken() As SqlToken
    Set getCommaToken = New SqlToken
    getCommaToken.Init ",", SqlTokenType.COMMA
End Function

' 生成一个函数名称元素
Private Function createFuncNameToken(ByVal funcName As String) As SqlToken
    Set createFuncNameToken = New SqlToken
    createFuncNameToken.Init funcName, SqlTokenType.STR_FUNC
End Function

' 获取 CURRENT_TIMESTAMP 元素
Private Function getCurrentTimestampToken() As SqlToken
    Set getCurrentTimestampToken = New SqlToken
    getCurrentTimestampToken.Init "CURRENT_TIMESTAMP", SqlTokenType.CONST_CURRENT_TIMESTAMP
End Function

' 获取 exists 元素
Private Function getExistsToken() As SqlToken
    Set getExistsToken = New SqlToken
    getExistsToken.Init "exists", SqlTokenType.KEY_EXISTS
End Function

' 获取 select 关键字
Private Function getSelectToken() As SqlToken
    Set getSelectToken = New SqlToken
    getSelectToken.Init "select", SqlTokenType.KEY_SELECT
End Function

' 获取一个 from 元素
Private Function getFromToken() As SqlToken
    Set getFromToken = New SqlToken
    getFromToken.Init "from", SqlTokenType.KEY_FROM
End Function

' ======================================================================
' Sql 解析方法的测试
' ======================================================================

' 测试: 匹配Exists表达式
Private Sub test_parseExistsExpr()
    Dim analyzer As SqlFormatterAnalyzer
    Set analyzer = createSqlFormatterAnalyzer

    Dim config As SqlFormatterConfiguration
    Set config = analyzer.getConfig

    Dim scanner As ObjArrayRefScanner
    Dim elem As ISqlFormatterElem
    Dim token As SqlToken
    Dim strArr As IPrimitiveArrayRef
    Dim stack As ObjStack

    ' ==============================
    ' 1. 扫描器内:  Exists
    '    测试: 无左括号栈 + 只有一个 Exists
    ' ==============================
    ' 1. 创建扫描器
    Set token = getExistsToken
    Set scanner = getObjArrayRefScanner(token)
    ' 2. 执行测试
    Set elem = analyzer.parseExistsExpr(scanner, Nothing)
    ' 3. 检查结果
    Debug.Assert elem Is token
    Debug.Assert scanner.hasNext = False

    ' ==============================
    ' 2. 扫描器内:  Exists + UNION
    '    测试: 无左括号栈 + 只有一个 Exists + exists 之后的内容无法解析出一个 select 作用域
    ' ==============================
    ' 1. 创建扫描器
    Set token = getExistsToken
    Set scanner = getObjArrayRefScanner(token, getUnionToken)
    ' 2. 执行测试
    Set elem = analyzer.parseExistsExpr(scanner, Nothing)
    ' 3. 检查结果
    Debug.Assert elem Is token
    Debug.Assert scanner.hasNext = True
    Set token = scanner.getNext
    Debug.Assert token.tokenType = SqlTokenType.KEY_UNION

    ' ==============================
    ' 3. 扫描器内:  Exists
    '            + (
    '                  + select + STR_NAME
    '                  + from + STR_NAME
    '            + )
    '    测试: 无左括号栈 + 只有一个 Exists + exists 之后的内容无法解析出一个 select 作用域
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getExistsToken, _
        getLeftParenthesisToken, _
        _
        getSelectToken, getStrNameToken("x1"), _
        getFromToken, getStrNameToken("x2"), _
        _
        getRightParenthesisToken)
    ' 2. 执行测试
    Set elem = analyzer.parseExistsExpr(scanner, Nothing)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterTwoElemCondition
    Debug.Assert scanner.hasNext = False

    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 6
    Debug.Assert strArr.Item(0) = "exists ("
    Debug.Assert strArr.Item(1) = "    select"
    Debug.Assert strArr.Item(2) = "        x1"
    Debug.Assert strArr.Item(3) = "    from"
    Debug.Assert strArr.Item(4) = "        x2"
    Debug.Assert strArr.Item(5) = ")"

    ' ==============================
    ' 4. 扫描器内: Exists
    '            + (
    '                  + select + STR_NAME
    '                  + from + STR_NAME
    '            + )
    '            + UNION
    '    测试: 无左括号栈 + 只有一个 Exists + exists 的 select 作用域后面会处理 UNION
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getExistsToken, _
        getLeftParenthesisToken, _
        _
        getSelectToken, getStrNameToken("x1"), _
        getFromToken, getStrNameToken("x2"), _
        _
        getRightParenthesisToken, getUnionToken)
    ' 2. 执行测试
    Set elem = analyzer.parseExistsExpr(scanner, Nothing)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterTwoElemCondition
    Debug.Assert scanner.hasNext = False

    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 7
    Debug.Assert strArr.Item(0) = "exists ("
    Debug.Assert strArr.Item(1) = "    select"
    Debug.Assert strArr.Item(2) = "        x1"
    Debug.Assert strArr.Item(3) = "    from"
    Debug.Assert strArr.Item(4) = "        x2"
    Debug.Assert strArr.Item(5) = ")"
    Debug.Assert strArr.Item(6) = "union"

    ' ==============================
    ' 5. 扫描器内: Exists
    '            + (
    '                  + select + STR_NAME
    '                  + from + STR_NAME
    '            + )
    '            + JOIN
    '    测试: 无左括号栈 + 只有一个 Exists + exists 的 select 作用域后面不会处理 UNION 之外的内容
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getExistsToken, _
        getLeftParenthesisToken, _
        _
        getSelectToken, getStrNameToken("x1"), _
        getFromToken, getStrNameToken("x2"), _
        _
        getRightParenthesisToken, getJoinToken)
    ' 2. 执行测试
    Set elem = analyzer.parseExistsExpr(scanner, Nothing)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterTwoElemCondition
    Debug.Assert scanner.hasNext = True
    Set token = scanner.getNext
    Debug.Assert token.tokenType = SqlTokenType.KEY_JOIN

    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 6
    Debug.Assert strArr.Item(0) = "exists ("
    Debug.Assert strArr.Item(1) = "    select"
    Debug.Assert strArr.Item(2) = "        x1"
    Debug.Assert strArr.Item(3) = "    from"
    Debug.Assert strArr.Item(4) = "        x2"
    Debug.Assert strArr.Item(5) = ")"

    ' ==============================
    ' 6. 扫描器内: Exists
    '            + (
    '                  + select + STR_NAME
    '                  + from + STR_NAME
    '            + )
    '             + ) + 一个注释 + )
    '    测试: 左括号栈 + 只有一个 Exists + exists 的 select 作用域
    '          左括号栈内=一个左括号 + 一个左括号 + 一个注释 + 一个左括号
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getExistsToken, _
        getLeftParenthesisToken, _
        _
        getSelectToken, getStrNameToken("x1"), _
        getFromToken, getStrNameToken("x2"), _
        _
        getRightParenthesisToken, _
        getRightParenthesisToken, createShareLineCommentToken("--c2"), getRightParenthesisToken)
    ' 2. 创建左括号栈
    Set stack = New ObjStack
    stack.push getLeftParenthesisToken
    stack.push getLeftParenthesisToken
    stack.push createShareLineCommentToken("--c1")
    stack.push getLeftParenthesisToken

    Debug.Assert stack.Count = 4

    ' 3. 执行测试
    Set elem = analyzer.parseExistsExpr(scanner, stack)
    ' 4. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterContainerCol
    Debug.Assert scanner.hasNext = False

    Debug.Assert stack.Count = 1
    Set token = stack.pop
    Debug.Assert token.tokenType = SqlTokenType.LEFT_PARENTHESIS

    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 10
    Debug.Assert strArr.Item(0) = "( --c1"
    Debug.Assert strArr.Item(1) = "    ("
    Debug.Assert strArr.Item(2) = "        exists ("
    Debug.Assert strArr.Item(3) = "            select"
    Debug.Assert strArr.Item(4) = "                x1"
    Debug.Assert strArr.Item(5) = "            from"
    Debug.Assert strArr.Item(6) = "                x2"
    Debug.Assert strArr.Item(7) = "        )"
    Debug.Assert strArr.Item(8) = "    ) --c2"
    Debug.Assert strArr.Item(9) = ")"

    ' ==============================
    ' 7. 扫描器内: exists
    '             + (
    '             + select + STR_NAME
    '             + from
    '                       + (
    '                       + select + STR_NAME
    '                       + from + STR_NAME
    '                       + )
    '             + where
    '                   + STR_NAME + <= + STR_NAME
    '                   + and + not + exists + (
    '                       + select + STR_NAME
    '                       + from + STR_NAME
    '                   + )
    '             + )
    '    测试: 只匹配到一个 select 开头的子查询
    '         + 子查询中也包含子查询 + 查询条件中也包含子查询
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getExistsToken, _
        getLeftParenthesisToken, _
        _
        getSelectToken, getStrNameToken("a2"), _
        getFromToken, _
        _
        getLeftParenthesisToken, _
        getSelectToken, getStrNameToken("b1"), _
        getFromToken, getStrNameToken("table2"), _
        getRightParenthesisToken, _
        _
        getWhereToken, getStrNameToken("q1"), getLeConditionOperator, getStrNameToken("q2"), _
        _
        getAndToken, getNotToken, getExistsToken, getLeftParenthesisToken, _
        getSelectToken, getStrNameToken("e1"), getFromToken, getStrNameToken("subTable"), _
        getRightParenthesisToken, _
        _
        getAndToken, getStrNameToken("q3"), getLeConditionOperator, getStrNameToken("q4"), _
        _
        getRightParenthesisToken)
    ' 2. 执行测试方法
    Set elem = analyzer.parseExistsExpr(scanner, stack)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterTwoElemCondition
    Debug.Assert scanner.hasNext = False

    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 20
    Debug.Assert strArr.Item(0)  = "exists ("
    Debug.Assert strArr.Item(1)  = "    select"
    Debug.Assert strArr.Item(2)  = "        a2"
    Debug.Assert strArr.Item(3)  = "    from"
    Debug.Assert strArr.Item(4)  = "        ("
    Debug.Assert strArr.Item(5)  = "            select"
    Debug.Assert strArr.Item(6)  = "                b1"
    Debug.Assert strArr.Item(7)  = "            from"
    Debug.Assert strArr.Item(8)  = "                table2"
    Debug.Assert strArr.Item(9)  = "        )"
    Debug.Assert strArr.Item(10) = "    where"
    Debug.Assert strArr.Item(11) = "        q1 <= q2"
    Debug.Assert strArr.Item(12) = "        and not exists ("
    Debug.Assert strArr.Item(13) = "                select"
    Debug.Assert strArr.Item(14) = "                    e1"
    Debug.Assert strArr.Item(15) = "                from"
    Debug.Assert strArr.Item(16) = "                    subTable"
    Debug.Assert strArr.Item(17) = "            )"
    Debug.Assert strArr.Item(18) = "        and q3 <= q4"
    Debug.Assert strArr.Item(19) = ")"

End Sub

' 测试: 匹配真实列或全数据列
Private Sub test_parseColFromStrName()
    Dim analyzer As SqlFormatterAnalyzer
    Set analyzer = createSqlFormatterAnalyzer

    Dim config As SqlFormatterConfiguration
    Set config = analyzer.getConfig

    Dim scanner As ObjArrayRefScanner
    Dim elem As ISqlFormatterElem
    Dim token As SqlToken
    Dim strArr As IPrimitiveArrayRef

    ' ==============================
    ' 1. colName
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner( _
        getStrNameToken("x1"), _
        getStrNameToken("x2") _
    )

    ' 2. 执行测试
    Set elem = analyzer.parseColFromStrName(scanner, Nothing)

    ' 3. 检查结果
    Debug.Assert elem.toString(config) = "x1"
    '    检查: 扫描器没用完，并且下一个是 x2
    Debug.Assert scanner.hasNext = True
    Set token = scanner.getNext
    Debug.Assert token.Value = "x2"

    ' 4. 附加注释再测试
    Set scanner = getObjArrayRefScanner( _
        getStrNameToken("x1"), _
        createShareLineCommentToken("--c1"), _
        getStrNameToken("x2") _
    )

    Set elem = analyzer.parseColFromStrName(scanner, Nothing)

    Debug.Assert elem.toString(config) = "x1 --c1"
    Debug.Assert scanner.hasNext = True
    Set token = scanner.getNext
    Debug.Assert token.Value = "x2"

    ' ==============================
    ' 2. *
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner( _
        getAsteriskToken, _
        getStrNameToken("x2") _
    )

    ' 2. 执行测试
    Set elem = analyzer.parseColFromStrName(scanner, Nothing)

    ' 3. 检查结果
    Debug.Assert elem.toString(config) = "*"
    '    检查: 扫描器没用完，并且下一个是 x2
    Debug.Assert scanner.hasNext = True
    Set token = scanner.getNext
    Debug.Assert token.Value = "x2"

    ' 4. 附加注释再测试
    Set scanner = getObjArrayRefScanner( _
        getAsteriskToken, _
        createShareLineCommentToken("--c1"), _
        getStrNameToken("x2") _
    )

    Set elem = analyzer.parseColFromStrName(scanner, Nothing)

    Debug.Assert elem.toString(config) = "* --c1"
    Debug.Assert scanner.hasNext = True
    Set token = scanner.getNext
    Debug.Assert token.Value = "x2"

    ' ==============================
    ' 3. tableName.colName
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner( _
        getStrNameToken("t1"), _
        getPointToken, _
        getStrNameToken("c1"), _
        getStrNameToken("x2") _
    )

    ' 2. 执行测试
    Set elem = analyzer.parseColFromStrName(scanner, Nothing)

    ' 3. 检查结果
    Debug.Assert elem.toString(config) = "t1.c1"
    '    检查: 扫描器没用完，并且下一个是 x2
    Debug.Assert scanner.hasNext = True
    Set token = scanner.getNext
    Debug.Assert token.Value = "x2"

    ' 4. 附加注释再测试
    Set scanner = getObjArrayRefScanner( _
        getStrNameToken("t1"), _
        getPointToken, _
        getStrNameToken("c1"), _
        createShareLineCommentToken("--c1"), _
        getStrNameToken("x2") _
    )

    Set elem = analyzer.parseColFromStrName(scanner, Nothing)

    Debug.Assert elem.toString(config) = "t1.c1 --c1"
    Debug.Assert scanner.hasNext = True
    Set token = scanner.getNext
    Debug.Assert token.Value = "x2"

    ' ==============================
    ' 4. tableName.*
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner( _
        getStrNameToken("t1"), _
        getPointToken, _
        getAsteriskToken, _
        getStrNameToken("x2") _
    )

    ' 2. 执行测试
    Set elem = analyzer.parseColFromStrName(scanner, Nothing)

    ' 3. 检查结果
    Debug.Assert elem.toString(config) = "t1.*"
    '    检查: 扫描器没用完，并且下一个是 x2
    Debug.Assert scanner.hasNext = True
    Set token = scanner.getNext
    Debug.Assert token.Value = "x2"

    ' 4. 附加注释再测试
    Set scanner = getObjArrayRefScanner( _
        getStrNameToken("t1"), _
        getPointToken, _
        getAsteriskToken, _
        createShareLineCommentToken("--c1"), _
        getStrNameToken("x2") _
    )

    Set elem = analyzer.parseColFromStrName(scanner, Nothing)

    Debug.Assert elem.toString(config) = "t1.* --c1"
    Debug.Assert scanner.hasNext = True
    Set token = scanner.getNext
    Debug.Assert token.Value = "x2"

    ' ==============================
    ' 5. tableName.
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner( _
        getStrNameToken("t1"), _
        getPointToken, _
        getJoinToken _
    )

    ' 2. 执行测试
    Set elem = analyzer.parseColFromStrName(scanner, Nothing)

    ' 3. 检查结果
    Debug.Assert elem.toString(config) = "t1."
    '    检查: 扫描器没用完，并且下一个是 join
    Debug.Assert scanner.hasNext = True
    Set token = scanner.getNext
    Debug.Assert token.Value = "join"

    ' 4. 附加注释再测试
    Set scanner = getObjArrayRefScanner( _
        getStrNameToken("t1"), _
        getPointToken, _
        createShareLineCommentToken("--c1"), _
        getJoinToken _
    )

    Set elem = analyzer.parseColFromStrName(scanner, Nothing)

    Debug.Assert elem.toString(config) = "t1. --c1"
    Debug.Assert scanner.hasNext = True
    Set token = scanner.getNext
    Debug.Assert token.Value = "join"

    ' ==============================
    ' 6. tableName.colName + 边界括号
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner( _
        getStrNameToken("t1"), _
        getPointToken, _
        getStrNameToken("c1"), _
        getRightParenthesisToken _
    )

    ' 2. 向栈对象中压入: 一个左括号
    Dim stack As ObjStack
    Set stack = New ObjStack
    stack.push getLeftParenthesisToken

    ' 2. 执行测试
    Set elem = analyzer.parseColFromStrName(scanner, stack)

    ' 3. 检查结果
    Debug.Assert elem.toString(config) = "(t1.c1)"

    ' ==============================
    ' 7. 只有 colName
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getStrNameToken("t1"), getRightParenthesisToken)
    ' 2. 向栈对象中压入: 一个左括号
    Set stack = New ObjStack
    stack.push getLeftParenthesisToken
    ' 2. 执行测试
    Set elem = analyzer.parseColFromStrName(scanner, stack)
    ' 3. 检查结果
    Debug.Assert elem.toString(config) = "(t1)"

End Sub

' 测试: 匹配任意形式的列表达式
Private Sub test_parseMixedColExpr()
    Dim analyzer As SqlFormatterAnalyzer
    Set analyzer = createSqlFormatterAnalyzer

    Dim config As SqlFormatterConfiguration
    Set config = analyzer.getConfig

    Dim token As SqlToken
    Dim scanner As ObjArrayRefScanner
    Dim elem As ISqlFormatterElem
    Dim strArr As IPrimitiveArrayRef

    ' ==============================
    ' 1. 扫描器内:  STR_NAME + 参数[needStopInFirstLogic = true]
    ' ==============================
    Set token = getStrNameToken("x1")
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(token)
    ' 2. 执行测试方法
    Set elem = analyzer.parseMixedColExpr(scanner, True)
    ' 3. 检查结果
    Debug.Assert elem Is token
    Debug.Assert scanner.hasNext = False

    ' ==============================
    ' 2. 扫描器内:  STR_NAME + 参数[needStopInFirstLogic = false]
    ' ==============================
    Set token = getStrNameToken("x1")
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(token)
    ' 2. 执行测试方法
    Set elem = analyzer.parseMixedColExpr(scanner, False)
    ' 3. 检查结果
    Debug.Assert elem Is token
    Debug.Assert scanner.hasNext = False

    ' ==============================
    ' 3. 扫描器内:  STR_NAME + STR_NAME
    ' ==============================
    Set token = getStrNameToken("x1")
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(token, getStrNameToken("x2"))
    ' 2. 执行测试方法
    Set elem = analyzer.parseMixedColExpr(scanner, False)
    ' 3. 检查结果
    Debug.Assert elem Is token
    Debug.Assert scanner.hasNext = True
    Set token = scanner.getNext
    Debug.Assert token.Value = "x2"

    ' ==============================
    ' 4. 扫描器内:  STR_NAME + [+] + 参数[needStopInFirstLogic = false]
    ' ==============================
    Set token = getStrNameToken("x1")
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(token, getPlusToken)
    ' 2. 执行测试方法
    Set elem = analyzer.parseMixedColExpr(scanner, False)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterExprCol
    Debug.Assert scanner.hasNext = False
    Debug.Assert elem.toString(config) = "x1 +"

    ' ==============================
    ' 5. 扫描器内: STR_NAME + [+] + STR_NAME + 参数[needStopInFirstLogic = false]
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getStrNameToken("x1"), getPlusToken, getStrNameToken("x2"))
    ' 2. 执行测试方法
    Set elem = analyzer.parseMixedColExpr(scanner, False)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterExprCol
    Debug.Assert scanner.hasNext = False
    Debug.Assert elem.toString(config) = "x1 + x2"

    ' ==============================
    ' 6. 扫描器内: STR_NAME + [-] + STR_NAME + UNION + 参数[needStopInFirstLogic = false]
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner( _
        getStrNameToken("x1"), getPlusToken, getStrNameToken("x2"), _
        getUnionToken)
    ' 2. 执行测试方法
    Set elem = analyzer.parseMixedColExpr(scanner, False)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterExprCol
    Debug.Assert scanner.hasNext = True
    Set token = scanner.getNext
    Debug.Assert token.tokenType = SqlTokenType.KEY_UNION
    Debug.Assert elem.toString(config) = "x1 + x2"

    ' ==============================
    ' 7. 扫描器内: STR_NAME + 一个注释 + [+] + 一个注释 + STR_NAME + 一个注释 + 参数[needStopInFirstLogic = false]
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner( _
        getStrNameToken("x1"), _
        createShareLineCommentToken("--c1"), _
        getPlusToken, _
        createShareLineCommentToken("--c2"), _
        getStrNameToken("x2"), _
        createShareLineCommentToken("--c3"))
    ' 2. 执行测试方法
    Set elem = analyzer.parseMixedColExpr(scanner, False)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterExprCol
    Debug.Assert scanner.hasNext = False

    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 3
    Debug.Assert strArr.Item(0) = "x1 --c1"
    Debug.Assert strArr.Item(1) = "    + --c2"
    Debug.Assert strArr.Item(2) = "    x2 --c3"

    ' ==============================
    ' 8. 扫描器内: STR_NAME + 一个注释 + <=
    '            + 一个注释 + STR_NAME + 一个注释 + 参数[needStopInFirstLogic = false]
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner( _
        getStrNameToken("x1"), _
        createShareLineCommentToken("--c1"), _
        getLeConditionOperator, _
        createShareLineCommentToken("--c2"), _
        getStrNameToken("x2"), _
        createShareLineCommentToken("--c3"))
    ' 2. 执行测试方法
    Set elem = analyzer.parseMixedColExpr(scanner, False)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterTwoElemCondition
    Debug.Assert scanner.hasNext = False

    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 3
    Debug.Assert strArr.Item(0) = "x1 --c1"
    Debug.Assert strArr.Item(1) = "<= --c2"
    Debug.Assert strArr.Item(2) = "x2 --c3"

    ' ==============================
    ' 9. 扫描器内: STR_NAME + 一个注释 + <= + 
    '    一个注释 + STR_NAME + 一个注释 + 参数[needStopInFirstLogic = true]
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner( _
        getStrNameToken("x1"), _
        createShareLineCommentToken("--c1"), _
        getLeConditionOperator, _
        createShareLineCommentToken("--c2"), _
        getStrNameToken("x2"), _
        createShareLineCommentToken("--c3"))
    ' 2. 执行测试方法
    Set elem = analyzer.parseMixedColExpr(scanner, True)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterContainerCol
    Debug.Assert scanner.hasNext = True
    Set token = scanner.getNext
    Debug.Assert token.tokenType = SqlTokenType.OPERA_LE

    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 1
    Debug.Assert strArr.Item(0) = "x1 --c1"

    ' ==============================
    ' 10. 扫描器内: STR_NAME + 一个注释
    '            + and + 一个注释
    '            + STR_NAME + <= + 一个注释 + STR_NAME
    '            + 参数[needStopInFirstLogic = false]
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner( _
        getStrNameToken("x1"), _
        createShareLineCommentToken("--c1"), _
        getAndToken, _
        createShareLineCommentToken("--c2"), _
        getStrNameToken("x2"), _
        getLeConditionOperator, _
        createShareLineCommentToken("--c3"), _
        getStrNameToken("x3"))
    ' 2. 执行测试方法
    Set elem = analyzer.parseMixedColExpr(scanner, False)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterMultiConditions
    Debug.Assert scanner.hasNext = False

    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 4
    Debug.Assert strArr.Item(0) = "x1 --c1"
    Debug.Assert strArr.Item(1) = "and --c2"
    Debug.Assert strArr.Item(2) = "    x2 <= --c3"
    Debug.Assert strArr.Item(3) = "          x3"

    ' ==============================
    ' 11. 扫描器内: 一个 STR_NAME + 一个注释
    '            + 一个 and + 一个注释
    '            + 一个 STR_NAME + <= + 一个注释 + STR_NAME
    '            + 参数[needStopInFirstLogic = true]
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner( _
        getStrNameToken("x1"), _
        createShareLineCommentToken("--c1"), _
        getAndToken, _
        createShareLineCommentToken("--c2"), _
        getStrNameToken("x2"), _
        getLeConditionOperator, _
        createShareLineCommentToken("--c3"), _
        getStrNameToken("x3"))
    ' 2. 执行测试方法
    Set elem = analyzer.parseMixedColExpr(scanner, True)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterContainerCol
    Debug.Assert scanner.hasNext = True
    Set token = scanner.getNext
    Debug.Assert token.tokenType = SqlTokenType.KEY_AND

    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 1
    Debug.Assert strArr.Item(0) = "x1 --c1"

End Sub

' 测试: 匹配函数列对象
Private Sub test_parseFunction()
    Dim analyzer As SqlFormatterAnalyzer
    Set analyzer = createSqlFormatterAnalyzer

    Dim config As SqlFormatterConfiguration
    Set config = analyzer.getConfig

    Dim token As SqlToken
    Dim scanner As ObjArrayRefScanner
    Dim elem As ISqlFormatterElem
    Dim strArr As IPrimitiveArrayRef
    Dim stack As ObjStack

    ' ==============================
    ' 1. 扫描器内: 一个函数名
    '    无左括号栈
    ' ==============================
    ' 1. 创建扫描器
    Set token = createFuncNameToken("max")
    Set scanner = getObjArrayRefScanner(token)
    ' 2. 执行测试方法
    Set elem = analyzer.parseFunction(scanner, Nothing)
    ' 3. 检查结果
    Debug.Assert elem Is token
    Debug.Assert scanner.hasNext = False

    ' ==============================
    ' 2. 扫描器内: 一个函数名 + (
    '    无左括号栈
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(createFuncNameToken("max"), getLeftParenthesisToken)
    ' 2. 执行测试方法
    Set elem = analyzer.parseFunction(scanner, Nothing)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterFuncCol
    Debug.Assert scanner.hasNext = False
    Debug.Assert elem.toString(config) = "max("

    ' ==============================
    ' 3. 扫描器内: 一个函数名 + ( + STR_NAME + [,] + STR_NAME + )
    '    无左括号栈
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(createFuncNameToken("abc"), _
        getLeftParenthesisToken, _
        getStrNameToken("x1"), getCommaToken, getStrNameToken("x2"), _
        getRightParenthesisToken)
    ' 2. 执行测试方法
    Set elem = analyzer.parseFunction(scanner, Nothing)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterFuncCol
    Debug.Assert scanner.hasNext = False
    Set strArr = elem.toStringArray(config)
    Debug.Assert elem.toString(config) = "abc(x1, x2)"

    ' ==============================
    ' 4. 扫描器内: 一个函数名 + ( + STR_NAME + [,] + STR_NAME + ) + UNION(强制停止)
    '    无左括号栈
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(createFuncNameToken("abc"), _
        getLeftParenthesisToken, _
        getStrNameToken("x1"), getCommaToken, getStrNameToken("x2"), _
        getRightParenthesisToken, getUnionToken)
    ' 2. 执行测试方法
    Set elem = analyzer.parseFunction(scanner, Nothing)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterFuncCol
    Debug.Assert scanner.hasNext = True
    Set token = scanner.getNext()
    Debug.Assert token.tokenType = SqlTokenType.KEY_UNION
    Set strArr = elem.toStringArray(config)
    Debug.Assert elem.toString(config) = "abc(x1, x2)"

    ' ==============================
    ' 5. 扫描器内: 一个函数名 + ( + 一个注释 + STR_NAME + [,] + STR_NAME + )
    '    无左括号栈
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(createFuncNameToken("abc"), _
        getLeftParenthesisToken, createShareLineCommentToken("--c1"), _
        getStrNameToken("x1"), getCommaToken, getStrNameToken("x2"), _
        getRightParenthesisToken)
    ' 2. 执行测试方法
    Set elem = analyzer.parseFunction(scanner, Nothing)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterFuncCol
    Debug.Assert scanner.hasNext = False

    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 4
    Debug.Assert strArr.Item(0) = "abc( --c1"
    Debug.Assert strArr.Item(1) = "    x1,"
    Debug.Assert strArr.Item(2) = "    x2"
    Debug.Assert strArr.Item(3) = ")"

    ' ==============================
    ' 6. 扫描器内: 一个函数名 + ( + 一个注释 + STR_NAME + [,] + STR_NAME + ) + ) + UNION
    '    有左括号栈
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(createFuncNameToken("abc"), _
        getLeftParenthesisToken, createShareLineCommentToken("--c1"), _
        getStrNameToken("x1"), getCommaToken, getStrNameToken("x2"), _
        getRightParenthesisToken, _
        getRightParenthesisToken, getUnionToken)
    ' 2. 创建左括号栈
    Set stack = New ObjStack
    stack.push getLeftParenthesisToken
    ' 3. 执行测试方法
    Set elem = analyzer.parseFunction(scanner, stack)
    ' 4. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterFuncCol
    Debug.Assert scanner.hasNext = True
    Set token = scanner.getNext
    Debug.Assert token.tokenType = SqlTokenType.KEY_UNION

    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 6
    Debug.Assert strArr.Item(0) = "("
    Debug.Assert strArr.Item(1) = "    abc( --c1"
    Debug.Assert strArr.Item(2) = "        x1,"
    Debug.Assert strArr.Item(3) = "        x2"
    Debug.Assert strArr.Item(4) = "    )"
    Debug.Assert strArr.Item(5) = ")"

    ' ==============================
    ' 7. 扫描器内: 一个函数名 + ( +
    '                  + case
    '                  + when + STR_NAME + Then + STR_NAME
    '                  + end
    '            + [,]
    '                  + STR_NAME + [+]
    '                      + case
    '                      + when + STR_NAME + Then + STR_NAME
    '                      + end
    '            + )
    '    无左括号栈
    '    多行元素测试 + 表达式
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(createFuncNameToken("abc"), getLeftParenthesisToken, _
        _
        getCaseToken, _
        getWhenToken, getStrNameToken("x1"), getThenToken, getStrNameToken("x2"), _
        getEndToken, _
        _
        getCommaToken, _
        _
        getStrNameToken("y1"), getPlusToken, _
        getCaseToken, _
        getWhenToken, getStrNameToken("x3"), getThenToken, getStrNameToken("x4"), _
        getEndToken, _
        _
        getRightParenthesisToken)
    ' 2. 执行测试方法
    Set elem = analyzer.parseFunction(scanner, Nothing)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterFuncCol
    Debug.Assert scanner.hasNext = False

    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 9
    Debug.Assert strArr.Item(0) = "abc("
    Debug.Assert strArr.Item(1) = "    case"
    Debug.Assert strArr.Item(2) = "        when x1 then x2"
    Debug.Assert strArr.Item(3) = "    end,"
    Debug.Assert strArr.Item(4) = "    y1 +"
    Debug.Assert strArr.Item(5) = "        case"
    Debug.Assert strArr.Item(6) = "            when x3 then x4"
    Debug.Assert strArr.Item(7) = "        end"
    Debug.Assert strArr.Item(8) = ")"
End Sub

' 测试: 匹配括号包裹的多个列对象
Private Sub test_parseParenthesisWrapperMultiCol()
    Dim analyzer As SqlFormatterAnalyzer
    Set analyzer = createSqlFormatterAnalyzer

    Dim config As SqlFormatterConfiguration
    Set config = analyzer.getConfig

    Dim token As SqlToken
    Dim scanner As ObjArrayRefScanner
    Dim elem As ISqlFormatterElem
    Dim strArr As IPrimitiveArrayRef

    ' ==============================
    ' 1. 扫描器内: 一个左括号
    ' ==============================
    ' 1. 创建扫描器
    Set token = getLeftParenthesisToken
    Set scanner = getObjArrayRefScanner(token)
    ' 2. 执行测试方法
    Set elem = analyzer.parseParenthesisWrapperMultiCol(scanner)
    ' 3. 检查结果
    Debug.Assert elem Is token
    Debug.Assert scanner.hasNext = False

    ' ==============================
    ' 2. 扫描器内: ( + 一个注释
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getLeftParenthesisToken, createShareLineCommentToken("--c1"))
    ' 2. 执行测试方法
    Set elem = analyzer.parseParenthesisWrapperMultiCol(scanner)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterCommaDeliMultiElem
    Debug.Assert scanner.hasNext = False
    Debug.Assert elem.toString(config) = "( --c1"

    ' ==============================
    ' 3. 扫描器内: ( + 一个注释 + UNION (强制结束)
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getLeftParenthesisToken, _
        createShareLineCommentToken("--c1"), getUnionToken)
    ' 2. 执行测试方法
    Set elem = analyzer.parseParenthesisWrapperMultiCol(scanner)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterCommaDeliMultiElem
    Debug.Assert scanner.hasNext = True
    Set token = scanner.getNext
    Debug.Assert token.tokenType = SqlTokenType.KEY_UNION
    Debug.Assert elem.toString(config) = "( --c1"

    ' ==============================
    ' 4. 扫描器内: ( + 一个注释 + UNION (强制结束)
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getLeftParenthesisToken, _
        createShareLineCommentToken("--c1"), getUnionToken)
    ' 2. 执行测试方法
    Set elem = analyzer.parseParenthesisWrapperMultiCol(scanner)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterCommaDeliMultiElem
    Debug.Assert scanner.hasNext = True
    Set token = scanner.getNext
    Debug.Assert token.tokenType = SqlTokenType.KEY_UNION
    Debug.Assert elem.toString(config) = "( --c1"

    ' ==============================
    ' 5. 扫描器内: ( + 一个注释 + STR_NAME + UNION (强制结束)
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getLeftParenthesisToken, _
        createShareLineCommentToken("--c1"), _
        getStrNameToken("x1"), _
        getUnionToken)
    ' 2. 执行测试方法
    Set elem = analyzer.parseParenthesisWrapperMultiCol(scanner)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterCommaDeliMultiElem
    Debug.Assert scanner.hasNext = True
    Set token = scanner.getNext
    Debug.Assert token.tokenType = SqlTokenType.KEY_UNION

    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 2
    Debug.Assert strArr.Item(0) = "( --c1"
    Debug.Assert strArr.Item(1) = "    x1"

    ' ==============================
    ' 6. 扫描器内: 一个左括号 + 一个元素 + 一个 UNION (强制结束)
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getLeftParenthesisToken, _
        getStrNameToken("x1"), _
        getUnionToken)
    ' 2. 执行测试方法
    Set elem = analyzer.parseParenthesisWrapperMultiCol(scanner)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterCommaDeliMultiElem
    Debug.Assert scanner.hasNext = True
    Set token = scanner.getNext
    Debug.Assert token.tokenType = SqlTokenType.KEY_UNION

    Debug.Assert elem.toString(config) = "(x1"

    ' ==============================
    ' 7. 扫描器内: ( + STR_NAME + [,]
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getLeftParenthesisToken, _
        getStrNameToken("x1"), getCommaToken)
    ' 2. 执行测试方法
    Set elem = analyzer.parseParenthesisWrapperMultiCol(scanner)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterCommaDeliMultiElem
    Debug.Assert scanner.hasNext = False
    Debug.Assert elem.toString(config) = "(x1,"

    ' ==============================
    ' 8. 扫描器内: ( + STR_NAME + [,] + UNION (强制结束)
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getLeftParenthesisToken, _
        getStrNameToken("x1"), getCommaToken, _
        getUnionToken)
    ' 2. 执行测试方法
    Set elem = analyzer.parseParenthesisWrapperMultiCol(scanner)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterCommaDeliMultiElem
    Debug.Assert scanner.hasNext = True
    Set token = scanner.getNext
    Debug.Assert token.tokenType = SqlTokenType.KEY_UNION

    Debug.Assert elem.toString(config) = "(x1,"

    ' ==============================
    ' 9. 扫描器内: ( + STR_NAME + [,] + 一个注释 + UNION (强制结束)
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getLeftParenthesisToken, _
        getStrNameToken("x1"), getCommaToken, createShareLineCommentToken("--c1"), _
        getUnionToken)
    ' 2. 执行测试方法
    Set elem = analyzer.parseParenthesisWrapperMultiCol(scanner)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterCommaDeliMultiElem
    Debug.Assert scanner.hasNext = True
    Set token = scanner.getNext
    Debug.Assert token.tokenType = SqlTokenType.KEY_UNION

    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 2
    Debug.Assert strArr.Item(0) = "("
    Debug.Assert strArr.Item(1) = "    x1, --c1"

    ' ==============================
    ' 10. 扫描器内: ( + STR_NAME + [,] + STR_NAME
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getLeftParenthesisToken, _
        getStrNameToken("x1"), getCommaToken, getStrNameToken("x2"))
    ' 2. 执行测试方法
    Set elem = analyzer.parseParenthesisWrapperMultiCol(scanner)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterCommaDeliMultiElem
    Debug.Assert scanner.hasNext = False
    Debug.Assert elem.toString(config) = "(x1, x2"

    ' ==============================
    ' 11. 扫描器内: ( + STR_NAME + [,] + STR_NAME + UNION (强制结束)
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getLeftParenthesisToken, _
        getStrNameToken("x1"), getCommaToken, getStrNameToken("x2"), _
        getUnionToken)
    ' 2. 执行测试方法
    Set elem = analyzer.parseParenthesisWrapperMultiCol(scanner)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterCommaDeliMultiElem
    Debug.Assert scanner.hasNext = True
    Set token = scanner.getNext
    Debug.Assert token.tokenType = SqlTokenType.KEY_UNION

    Debug.Assert elem.toString(config) = "(x1, x2"

    ' ==============================
    ' 12. 扫描器内: ( + STR_NAME + [,] + STR_NAME + )
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getLeftParenthesisToken, _
        getStrNameToken("x1"), getCommaToken, getStrNameToken("x2"), _
        getRightParenthesisToken)
    ' 2. 执行测试方法
    Set elem = analyzer.parseParenthesisWrapperMultiCol(scanner)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterCommaDeliMultiElem
    Debug.Assert scanner.hasNext = False
    Debug.Assert elem.toString(config) = "(x1, x2)"

    ' ==============================
    ' 13. 扫描器内: ( + STR_NAME + [,] + STR_NAME + [,] + STR_NAME + )
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getLeftParenthesisToken, _
        getStrNameToken("x1"), getCommaToken, getStrNameToken("x2"), _
        getCommaToken, getStrNameToken("x3"), _
        getRightParenthesisToken)
    ' 2. 执行测试方法
    Set elem = analyzer.parseParenthesisWrapperMultiCol(scanner)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterCommaDeliMultiElem
    Debug.Assert scanner.hasNext = False
    Debug.Assert elem.toString(config) = "(x1, x2, x3)"

    ' ==============================
    ' 14. 扫描器内: (
    '                  + case
    '                  + when + STR_NAME + Then + STR_NAME
    '                  + end
    '              + [,]
    '                  + STR_NAME + [+]
    '                      + case
    '                      + when + STR_NAME + Then + STR_NAME
    '                      + end
    '              + )
    '     多行元素测试 + 表达式
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getLeftParenthesisToken, _
        _
        getCaseToken, _
        getWhenToken, getStrNameToken("x1"), getThenToken, getStrNameToken("x2"), _
        getEndToken, _
        _
        getCommaToken, _
        _
        getStrNameToken("y1"), getPlusToken, _
        getCaseToken, _
        getWhenToken, getStrNameToken("x3"), getThenToken, getStrNameToken("x4"), _
        getEndToken, _
        _
        getRightParenthesisToken)
    ' 2. 执行测试方法
    Set elem = analyzer.parseParenthesisWrapperMultiCol(scanner)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterCommaDeliMultiElem
    Debug.Assert scanner.hasNext = False

    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 9
    Debug.Assert strArr.Item(0) = "("
    Debug.Assert strArr.Item(1) = "    case"
    Debug.Assert strArr.Item(2) = "        when x1 then x2"
    Debug.Assert strArr.Item(3) = "    end,"
    Debug.Assert strArr.Item(4) = "    y1 +"
    Debug.Assert strArr.Item(5) = "        case"
    Debug.Assert strArr.Item(6) = "            when x3 then x4"
    Debug.Assert strArr.Item(7) = "        end"
    Debug.Assert strArr.Item(8) = ")"

    ' ==============================
    ' 14. 扫描器内: ( + STR_NAME + [,] + STR_NAME + [,] + STR_NAME + )
    '     测试设置遇到逗号时需要换行
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getLeftParenthesisToken, _
        getStrNameToken("x1"), getCommaToken, getStrNameToken("x2"), _
        getCommaToken, getStrNameToken("x3"), _
        getRightParenthesisToken)
    ' 2. 执行测试方法
    Set elem = analyzer.parseParenthesisWrapperMultiCol(scanner, True)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterCommaDeliMultiElem
    Debug.Assert scanner.hasNext = False

    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 5
    Debug.Assert strArr.Item(0) = "("
    Debug.Assert strArr.Item(1) = "    x1,"
    Debug.Assert strArr.Item(2) = "    x2,"
    Debug.Assert strArr.Item(3) = "    x3"
    Debug.Assert strArr.Item(4) = ")"
End Sub

' 测试: 匹配 case 表达式
Private Sub test_parseCaseStatement()
    Dim analyzer As SqlFormatterAnalyzer
    Set analyzer = createSqlFormatterAnalyzer

    Dim config As SqlFormatterConfiguration
    Set config = analyzer.getConfig

    Dim token As SqlToken
    Dim scanner As ObjArrayRefScanner
    Dim elem As ISqlFormatterElem
    Dim strArr As IPrimitiveArrayRef

    ' ==============================
    ' 1. 扫描器内: case
    ' ==============================
    ' 1. 创建扫描器
    Set token = getCaseToken
    Set scanner = getObjArrayRefScanner(token)
    ' 2. 执行测试方法
    Set elem = analyzer.parseCaseStatement(scanner, Nothing)
    ' 3. 检查结果
    Debug.Assert elem Is token
    Debug.Assert scanner.hasNext = False

    ' ==============================
    ' 2. 扫描器内: case + UNION
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getCaseToken, getUnionToken)
    ' 2. 执行测试方法
    Set elem = analyzer.parseCaseStatement(scanner, Nothing)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterCaseCol
    Debug.Assert scanner.hasNext = True
    Set token = scanner.getNext
    Debug.Assert token.tokenType = SqlTokenType.KEY_UNION
    Debug.Assert elem.toString(config) = "case"

    ' ==============================
    ' 3. 扫描器内: case + 一个注释
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getCaseToken, createShareLineCommentToken("--c1"))
    ' 2. 执行测试方法
    Set elem = analyzer.parseCaseStatement(scanner, Nothing)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterCaseCol
    Debug.Assert scanner.hasNext = False

    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 1
    Debug.Assert strArr.Item(0) = "case --c1"

    ' ==============================
    ' 4. 扫描器内: case + 一个注释 + UNION
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getCaseToken, createShareLineCommentToken("--c1"), getUnionToken)
    ' 2. 执行测试方法
    Set elem = analyzer.parseCaseStatement(scanner, Nothing)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterCaseCol
    Debug.Assert scanner.hasNext = True
    Set token = scanner.getNext
    Debug.Assert token.tokenType = SqlTokenType.KEY_UNION

    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 1
    Debug.Assert strArr.Item(0) = "case --c1"

    ' ==============================
    ' 5. 扫描器内: case + STR_NAME + UNION
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getCaseToken, getStrNameToken("x1"), getUnionToken)
    ' 2. 执行测试方法
    Set elem = analyzer.parseCaseStatement(scanner, Nothing)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterCaseCol
    Debug.Assert scanner.hasNext = True
    Set token = scanner.getNext
    Debug.Assert token.tokenType = SqlTokenType.KEY_UNION

    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 2
    Debug.Assert strArr.Item(0) = "case"
    Debug.Assert strArr.Item(1) = "    x1"

    ' ==============================
    ' 6. 扫描器内: case + STR_NAME + 一个注释 + UNION
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getCaseToken, getStrNameToken("x1"), createShareLineCommentToken("--c1"), getUnionToken)
    ' 2. 执行测试方法
    Set elem = analyzer.parseCaseStatement(scanner, Nothing)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterCaseCol
    Debug.Assert scanner.hasNext = True
    Set token = scanner.getNext
    Debug.Assert token.tokenType = SqlTokenType.KEY_UNION

    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 2
    Debug.Assert strArr.Item(0) = "case"
    Debug.Assert strArr.Item(1) = "    x1 --c1"

    ' ==============================
    ' 7. 扫描器内: case + when
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getCaseToken, getWhenToken)
    ' 2. 执行测试方法
    Set elem = analyzer.parseCaseStatement(scanner, Nothing)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterCaseCol
    Debug.Assert scanner.hasNext = False

    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 2
    Debug.Assert strArr.Item(0) = "case"
    Debug.Assert strArr.Item(1) = "    when"

    ' ==============================
    ' 8. 扫描器内: case + else
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getCaseToken, getElseToken)
    ' 2. 执行测试方法
    Set elem = analyzer.parseCaseStatement(scanner, Nothing)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterCaseCol
    Debug.Assert scanner.hasNext = False

    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 2
    Debug.Assert strArr.Item(0) = "case"
    Debug.Assert strArr.Item(1) = "    else"

    ' ==============================
    ' 9. 扫描器内: case + end
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getCaseToken, getEndToken)
    ' 2. 执行测试方法
    Set elem = analyzer.parseCaseStatement(scanner, Nothing)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterCaseCol
    Debug.Assert scanner.hasNext = False

    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 2
    Debug.Assert strArr.Item(0) = "case"
    Debug.Assert strArr.Item(1) = "end"

    ' ==============================
    ' 10. 扫描器内: case + when + 一个注释 + end
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getCaseToken, getWhenToken, createShareLineCommentToken("--c1"),getEndToken)
    ' 2. 执行测试方法
    Set elem = analyzer.parseCaseStatement(scanner, Nothing)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterCaseCol
    Debug.Assert scanner.hasNext = False

    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 3
    Debug.Assert strArr.Item(0) = "case"
    Debug.Assert strArr.Item(1) = "    when --c1"
    Debug.Assert strArr.Item(2) = "end"

    ' ==============================
    ' 11. 扫描器内: case
    '             + when + STR_NAME + Then + STR_NAME + 一个注释
    '             + else + STR_NAME + 一个注释
    '             + end
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getCaseToken, _
        getWhenToken, getStrNameToken("x1"), getThenToken, getStrNameToken("x2"), createShareLineCommentToken("--c1"), _
        getElseToken, getStrNameToken("x3"), createShareLineCommentToken("--c2"), _
        getEndToken)
    ' 2. 执行测试方法
    Set elem = analyzer.parseCaseStatement(scanner, Nothing)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterCaseCol
    Debug.Assert scanner.hasNext = False

    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 4
    Debug.Assert strArr.Item(0) = "case"
    Debug.Assert strArr.Item(1) = "    when x1 then x2 --c1"
    Debug.Assert strArr.Item(2) = "    else x3 --c2"
    Debug.Assert strArr.Item(3) = "end"

    ' ==============================
    ' 12. 扫描器内: case
    '             + STR_NAME
    '             + when + STR_NAME + Then + STR_NAME + 一个注释
    '             + else + STR_NAME + 一个注释
    '             + end
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getCaseToken, _
        getStrNameToken("v"), _
        getWhenToken, getStrNameToken("x1"), getThenToken, getStrNameToken("x2"), createShareLineCommentToken("--c1"), _
        getElseToken, getStrNameToken("x3"), createShareLineCommentToken("--c2"), _
        getEndToken)
    ' 2. 执行测试方法
    Set elem = analyzer.parseCaseStatement(scanner, Nothing)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterCaseCol
    Debug.Assert scanner.hasNext = False

    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 5
    Debug.Assert strArr.Item(0) = "case"
    Debug.Assert strArr.Item(1) = "    v"
    Debug.Assert strArr.Item(2) = "    when x1 then x2 --c1"
    Debug.Assert strArr.Item(3) = "    else x3 --c2"
    Debug.Assert strArr.Item(4) = "end"

    ' ==============================
    ' 13. 扫描器内: case
    '             + STR_NAME
    '             + when + STR_NAME + Then + STR_NAME + 一个注释
    '             + else + STR_NAME + 一个注释
    '             + end
    '             + UNION
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getCaseToken, _
        getStrNameToken("v"), _
        getWhenToken, getStrNameToken("x1"), getThenToken, getStrNameToken("x2"), createShareLineCommentToken("--c1"), _
        getElseToken, getStrNameToken("x3"), createShareLineCommentToken("--c2"), _
        getEndToken, _
        getUnionToken)
    ' 2. 执行测试方法
    Set elem = analyzer.parseCaseStatement(scanner, Nothing)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterCaseCol
    Debug.Assert scanner.hasNext = True
    Set token = scanner.getNext
    Debug.Assert token.tokenType = SqlTokenType.KEY_UNION

    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 5
    Debug.Assert strArr.Item(0) = "case"
    Debug.Assert strArr.Item(1) = "    v"
    Debug.Assert strArr.Item(2) = "    when x1 then x2 --c1"
    Debug.Assert strArr.Item(3) = "    else x3 --c2"
    Debug.Assert strArr.Item(4) = "end"

    ' ==============================
    ' 14. 扫描器内: case
    '             + when + STR_NAME + Then + STR_NAME
    '             + else
    '                      + STR_NAME + PLUS
    '                      + case
    '                      + when + STR_NAME + Then + STR_NAME
    '                      + end
    '             + end
    '     多行元素测试 + 表达式
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getCaseToken, _
        getWhenToken, getStrNameToken("x1"), getThenToken, getStrNameToken("x2"), _
        getElseToken, _
        _
        getStrNameToken("y1"), getPlusToken, _
        getCaseToken, _
        getWhenToken, getStrNameToken("x3"), getThenToken, getStrNameToken("x4"), _
        getEndToken, _
        _
        getEndToken)

    ' 2. 执行测试方法
    Set elem = analyzer.parseCaseStatement(scanner, Nothing)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterCaseCol
    Debug.Assert scanner.hasNext = False

    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 8
    Debug.Assert strArr.Item(0) = "case"
    Debug.Assert strArr.Item(1) = "    when x1 then x2"
    Debug.Assert strArr.Item(2) = "    else"
    Debug.Assert strArr.Item(3) = "        y1 +"
    Debug.Assert strArr.Item(4) = "            case"
    Debug.Assert strArr.Item(5) = "                when x3 then x4"
    Debug.Assert strArr.Item(6) = "            end"
    Debug.Assert strArr.Item(7) = "end"

End Sub

' 测试： 匹配case中的when表达式
Private Sub test_parseCaseWhenStatement()
    Dim analyzer As SqlFormatterAnalyzer
    Set analyzer = createSqlFormatterAnalyzer

    Dim config As SqlFormatterConfiguration
    Set config = analyzer.getConfig

    Dim token As SqlToken
    Dim scanner As ObjArrayRefScanner
    Dim elem As ISqlFormatterElem
    Dim strArr As IPrimitiveArrayRef

    ' ==============================
    ' 1. 扫描器内: when
    ' ==============================
    ' 1. 创建扫描器
    Set token = getWhenToken
    Set scanner = getObjArrayRefScanner(token)
    ' 2. 执行测试方法
    Set elem = analyzer.parseCaseWhenStatement(scanner)
    ' 3. 检查结果
    Debug.Assert elem Is token
    Debug.Assert scanner.hasNext = False

    ' ==============================
    ' 2. 扫描器内: when + UNION
    ' ==============================
    ' 1. 创建扫描器
    Set token = getWhenToken
    Set scanner = getObjArrayRefScanner(token, getUnionToken)
    ' 2. 执行测试方法
    Set elem = analyzer.parseCaseWhenStatement(scanner)
    ' 3. 检查结果
    Debug.Assert elem Is token
    Debug.Assert scanner.hasNext = True
    Set token = scanner.getNext
    Debug.Assert token.tokenType = SqlTokenType.KEY_UNION

    ' ==============================
    ' 3. 扫描器内: when + 一个注释
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getWhenToken, createShareLineCommentToken("--c1"))
    ' 2. 执行测试方法
    Set elem = analyzer.parseCaseWhenStatement(scanner)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterCaseCompositionElem
    Debug.Assert scanner.hasNext = False
    Debug.Assert elem.toString(config) = "when --c1"

    ' ==============================
    ' 4. 扫描器内: when + 一个注释 + UNION
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getWhenToken, createShareLineCommentToken("--c1"), getUnionToken)
    ' 2. 执行测试方法
    Set elem = analyzer.parseCaseWhenStatement(scanner)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterCaseCompositionElem
    Debug.Assert scanner.hasNext = True
    Set token = scanner.getNext
    Debug.Assert token.tokenType = SqlTokenType.KEY_UNION

    Debug.Assert elem.toString(config) = "when --c1"

    ' ==============================
    ' 5. 扫描器内: when + 一个注释 + STR_NAME
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getWhenToken, createShareLineCommentToken("--c1"), getStrNameToken("x1"))
    ' 2. 执行测试方法
    Set elem = analyzer.parseCaseWhenStatement(scanner)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterCaseCompositionElem
    Debug.Assert scanner.hasNext = False
    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 2
    Debug.Assert strArr.Item(0) = "when --c1"
    Debug.Assert strArr.Item(1) = "    x1"

    ' ==============================
    ' 6. 扫描器内: when + 一个注释 + STR_NAME + UNION
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getWhenToken, createShareLineCommentToken("--c1"), getStrNameToken("x1"), _
        getUnionToken)
    ' 2. 执行测试方法
    Set elem = analyzer.parseCaseWhenStatement(scanner)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterCaseCompositionElem
    Debug.Assert scanner.hasNext = True
    Set token = scanner.getNext
    Debug.Assert token.tokenType = SqlTokenType.KEY_UNION

    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 2
    Debug.Assert strArr.Item(0) = "when --c1"
    Debug.Assert strArr.Item(1) = "    x1"

    ' ==============================
    ' 7. 扫描器内: when + STR_NAME + UNION
    '    when 后面没有注释
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getWhenToken, getStrNameToken("x1"), _
        getUnionToken)
    ' 2. 执行测试方法
    Set elem = analyzer.parseCaseWhenStatement(scanner)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterCaseCompositionElem
    Debug.Assert scanner.hasNext = True
    Set token = scanner.getNext
    Debug.Assert token.tokenType = SqlTokenType.KEY_UNION

    Debug.Assert elem.toString(config) = "when x1"

    ' ==============================
    ' 8. 扫描器内: when + STR_NAME + 一个注释 + then + 一个注释 + UNION
    '    then 前后有注释
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner( _
        getWhenToken, getStrNameToken("x1"), _
        createShareLineCommentToken("--c1"), _
        getThenToken, _
        createShareLineCommentToken("--c2"), _
        getUnionToken)
    ' 2. 执行测试方法
    Set elem = analyzer.parseCaseWhenStatement(scanner)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterCaseCompositionElem
    Debug.Assert scanner.hasNext = True
    Set token = scanner.getNext
    Debug.Assert token.tokenType = SqlTokenType.KEY_UNION

    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 3
    Debug.Assert strArr.Item(0) = "when"
    Debug.Assert strArr.Item(1) = "    x1 --c1"
    Debug.Assert strArr.Item(2) = "    then --c2"

    ' ==============================
    ' 9. 扫描器内: when + STR_NAME + then + UNION
    '    then 前后没有注释
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner( _
        getWhenToken, getStrNameToken("x1"), _
        getThenToken, _
        getUnionToken)
    ' 2. 执行测试方法
    Set elem = analyzer.parseCaseWhenStatement(scanner)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterCaseCompositionElem
    Debug.Assert scanner.hasNext = True
    Set token = scanner.getNext
    Debug.Assert token.tokenType = SqlTokenType.KEY_UNION

    Debug.Assert elem.toString(config) = "when x1 then"

    ' ==============================
    ' 10. 扫描器内: when + STR_NAME + 一个注释 + then + 一个注释 + STR_NAME + UNION
    '    then 前后有注释
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner( _
        getWhenToken, getStrNameToken("x1"), _
        createShareLineCommentToken("--c1"), _
        getThenToken, _
        createShareLineCommentToken("--c2"), _
        getStrNameToken("x2"), _
        getUnionToken)
    ' 2. 执行测试方法
    Set elem = analyzer.parseCaseWhenStatement(scanner)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterCaseCompositionElem
    Debug.Assert scanner.hasNext = True
    Set token = scanner.getNext
    Debug.Assert token.tokenType = SqlTokenType.KEY_UNION

    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 4
    Debug.Assert strArr.Item(0) = "when"
    Debug.Assert strArr.Item(1) = "    x1 --c1"
    Debug.Assert strArr.Item(2) = "    then --c2"
    Debug.Assert strArr.Item(3) = "    x2"

    ' ==============================
    ' 11. 扫描器内: when + STR_NAME + then + STR_NAME + UNION
    '    then 前后没有注释
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner( _
        getWhenToken, getStrNameToken("x1"), _
        getThenToken, _
        getStrNameToken("x2"), _
        getUnionToken)
    ' 2. 执行测试方法
    Set elem = analyzer.parseCaseWhenStatement(scanner)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterCaseCompositionElem
    Debug.Assert scanner.hasNext = True
    Set token = scanner.getNext
    Debug.Assert token.tokenType = SqlTokenType.KEY_UNION

    Debug.Assert elem.toString(config) = "when x1 then x2"

    ' ==============================
    ' 12. 扫描器内: when + STR_NAME + then + STR_NAME
    '    扫描时，扫描器用完
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner( _
        getWhenToken, getStrNameToken("x1"), _
        getThenToken, _
        getStrNameToken("x2"))
    ' 2. 执行测试方法
    Set elem = analyzer.parseCaseWhenStatement(scanner)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterCaseCompositionElem
    Debug.Assert scanner.hasNext = False

    Debug.Assert elem.toString(config) = "when x1 then x2"

    ' ==============================
    ' 13. 扫描器内: when
    '                  + case
    '                  + when + STR_NAME + Then + STR_NAME
    '                  + end
    '              + then
    '                  + STR_NAME + PLUS
    '                      + case
    '                      + when + STR_NAME + Then + STR_NAME
    '                      + end
    '     多行元素测试 + 表达式
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getWhenToken, _
        _
        getCaseToken, _
        getWhenToken, getStrNameToken("x1"), getThenToken, getStrNameToken("x2"), _
        getEndToken, _
        _
        getThenToken, _
        _
        getStrNameToken("y1"), getPlusToken, _
        getCaseToken, _
        getWhenToken, getStrNameToken("x3"), getThenToken, getStrNameToken("x4"), _
        getEndToken)
    ' 2. 执行测试方法
    Set elem = analyzer.parseCaseWhenStatement(scanner)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterCaseCompositionElem
    Debug.Assert scanner.hasNext = False

    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 9
    Debug.Assert strArr.Item(0) = "when"
    Debug.Assert strArr.Item(1) = "    case"
    Debug.Assert strArr.Item(2) = "        when x1 then x2"
    Debug.Assert strArr.Item(3) = "    end"
    Debug.Assert strArr.Item(4) = "    then"
    Debug.Assert strArr.Item(5) = "    y1 +"
    Debug.Assert strArr.Item(6) = "        case"
    Debug.Assert strArr.Item(7) = "            when x3 then x4"
    Debug.Assert strArr.Item(8) = "        end"

End Sub

' 测试: 匹配case中的else表达式
Private Sub test_parseCaseElseStatement()
    Dim analyzer As SqlFormatterAnalyzer
    Set analyzer = createSqlFormatterAnalyzer

    Dim config As SqlFormatterConfiguration
    Set config = analyzer.getConfig

    Dim token As SqlToken
    Dim scanner As ObjArrayRefScanner
    Dim elem As ISqlFormatterElem
    Dim strArr As IPrimitiveArrayRef

    ' ==============================
    ' 1. 扫描器内: 一个 else
    ' ==============================
    ' 1. 创建扫描器
    Set token = getElseToken
    Set scanner = getObjArrayRefScanner(token)
    ' 2. 执行测试方法
    Set elem = analyzer.parseCaseElseStatement(scanner)
    ' 3. 检查结果
    Debug.Assert elem Is token
    Debug.Assert scanner.hasNext = False

    ' ==============================
    ' 2. 扫描器内: else + UINON
    ' ==============================
    ' 1. 创建扫描器
    Set token = getElseToken
    Set scanner = getObjArrayRefScanner(token, getUnionToken)
    ' 2. 执行测试方法
    Set elem = analyzer.parseCaseElseStatement(scanner)
    ' 3. 检查结果
    Debug.Assert elem Is token
    Debug.Assert scanner.hasNext = True
    Set token = scanner.getNext
    Debug.Assert token.tokenType = SqlTokenType.KEY_UNION

    ' ==============================
    ' 3. 扫描器内: else + 一个注释
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getElseToken, createShareLineCommentToken("--c1"))
    ' 2. 执行测试方法
    Set elem = analyzer.parseCaseElseStatement(scanner)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterCaseCompositionElem
    Debug.Assert scanner.hasNext = False
    Debug.Assert elem.toString(config) = "else --c1"

    ' ==============================
    ' 4. 扫描器内: else + 一个注释 + UINON
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getElseToken, createShareLineCommentToken("--c1"), getUnionToken)
    ' 2. 执行测试方法
    Set elem = analyzer.parseCaseElseStatement(scanner)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterCaseCompositionElem
    Debug.Assert scanner.hasNext = True
    Set token = scanner.getNext
    Debug.Assert token.tokenType = SqlTokenType.KEY_UNION
    Debug.Assert elem.toString(config) = "else --c1"

    ' ==============================
    ' 5. 扫描器内: else + STR_NAME
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getElseToken, getStrNameToken("x1"))
    ' 2. 执行测试方法
    Set elem = analyzer.parseCaseElseStatement(scanner)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterCaseCompositionElem
    Debug.Assert scanner.hasNext = False
    Debug.Assert elem.toString(config) = "else x1"

    ' ==============================
    ' 6. 扫描器内: else + STR_NAME + UNION
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getElseToken, getStrNameToken("x1"), getUnionToken)
    ' 2. 执行测试方法
    Set elem = analyzer.parseCaseElseStatement(scanner)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterCaseCompositionElem
    Debug.Assert scanner.hasNext = True
    Set token = scanner.getNext
    Debug.Assert token.tokenType = SqlTokenType.KEY_UNION
    Debug.Assert elem.toString(config) = "else x1"

    ' ==============================
    ' 7. 扫描器内: else + 一个注释 + STR_NAME
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getElseToken, createShareLineCommentToken("--c1"), getStrNameToken("x1"))
    ' 2. 执行测试方法
    Set elem = analyzer.parseCaseElseStatement(scanner)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterCaseCompositionElem
    Debug.Assert scanner.hasNext = False

    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 2
    Debug.Assert strArr.Item(0) = "else --c1"
    Debug.Assert strArr.Item(1) = "    x1"

    ' ==============================
    ' 8. 扫描器内: else
    '             + case
    '             + when + STR_NAME + Then + STR_NAME
    '             + else + STR_NAME
    '             + end
    '    多行元素测试
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getElseToken, _
        getCaseToken, _
        getWhenToken, getStrNameToken("x1"), getThenToken, getStrNameToken("x2"), _
        getElseToken, getStrNameToken("x3"), _
        getEndToken)
    ' 2. 执行测试方法
    Set elem = analyzer.parseCaseElseStatement(scanner)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterCaseCompositionElem
    Debug.Assert scanner.hasNext = False

    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 5
    Debug.Assert strArr.Item(0) = "else"
    Debug.Assert strArr.Item(1) = "    case"
    Debug.Assert strArr.Item(2) = "        when x1 then x2"
    Debug.Assert strArr.Item(3) = "        else x3"
    Debug.Assert strArr.Item(4) = "    end"

    ' ==============================
    ' 9. 扫描器内: else
    '             + STR_NAME + PLUS
    '             + case
    '             + when + STR_NAME + Then + STR_NAME
    '             + end
    '    多行元素测试 + 表达式
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getElseToken, _
        getStrNameToken("y1"), getPlusToken, _
        getCaseToken, _
        getWhenToken, getStrNameToken("x1"), getThenToken, getStrNameToken("x2"), _
        getElseToken, getStrNameToken("x3"), _
        getEndToken)
    ' 2. 执行测试方法
    Set elem = analyzer.parseCaseElseStatement(scanner)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterCaseCompositionElem
    Debug.Assert scanner.hasNext = False

    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 6
    Debug.Assert strArr.Item(0) = "else"
    Debug.Assert strArr.Item(1) = "    y1 +"
    Debug.Assert strArr.Item(2) = "        case"
    Debug.Assert strArr.Item(3) = "            when x1 then x2"
    Debug.Assert strArr.Item(4) = "            else x3"
    Debug.Assert strArr.Item(5) = "        end"

End Sub

' 测试: 处理 cast 函数
Private Sub test_parseCastFunc()
    Dim analyzer As SqlFormatterAnalyzer
    Set analyzer = createSqlFormatterAnalyzer

    Dim config As SqlFormatterConfiguration
    Set config = analyzer.getConfig

    Dim token As SqlToken
    Dim scanner As ObjArrayRefScanner
    Dim elem As ISqlFormatterElem
    Dim strArr As IPrimitiveArrayRef
    Dim stack As ObjStack

    ' ==============================
    ' 1. 扫描器内: cast
    ' ==============================
    ' 1. 创建扫描器
    Set token = getCastToken
    Set scanner = getObjArrayRefScanner(token)
    ' 2. 执行测试方法
    Set elem = analyzer.parseCastFunc(scanner, Nothing)
    ' 3. 检查结果
    Debug.Assert elem Is token
    Debug.Assert scanner.hasNext = False

    ' ==============================
    ' 2. 扫描器内: cast + 一个注释
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getCastToken, createShareLineCommentToken("--c1"))
    ' 2. 执行测试方法
    Set elem = analyzer.parseCastFunc(scanner, Nothing)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterFuncCol
    Debug.Assert scanner.hasNext = False
    Debug.Assert elem.toString(config) = "cast --c1"

    ' ==============================
    ' 3. 扫描器内: cast + 一个注释 + UNION(强制停止)
    '    左括号栈: 一个左括号 (测试不吸收左括号)
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getCastToken, createShareLineCommentToken("--c1"), getUnionToken)
    Set stack = New ObjStack
    stack.push getLeftParenthesisToken

    ' 2. 执行测试方法
    Set elem = analyzer.parseCastFunc(scanner, stack)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterFuncCol

    Debug.Assert scanner.hasNext = True
    Set token = scanner.getNext
    Debug.Assert token.tokenType = SqlTokenType.KEY_UNION

    Debug.Assert elem.toString(config) = "cast --c1"
    Set token = stack.pop
    Debug.Assert token.tokenType = SqlTokenType.LEFT_PARENTHESIS

    ' ==============================
    ' 4. 扫描器内: cast + 一个注释 + ( + STR_NAME + AS + STR_NAME + ) + UNION(强制停止)
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getCastToken, createShareLineCommentToken("--c1"), _
        getLeftParenthesisToken, _
        getStrNameToken("x1"), getAsToken, getStrNameToken("int"), _
        getRightParenthesisToken, getUnionToken)

    ' 2. 执行测试方法
    Set elem = analyzer.parseCastFunc(scanner, Nothing)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterFuncCol

    Debug.Assert scanner.hasNext = True
    Set token = scanner.getNext
    Debug.Assert token.tokenType = SqlTokenType.KEY_UNION

    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 2
    Debug.Assert strArr.Item(0) = "cast --c1"
    Debug.Assert strArr.Item(1) = "(x1 as int)"

    ' ==============================
    ' 5. 扫描器内: cast + ( + STR_NAME + AS + STR_NAME + )
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getCastToken, _
        getLeftParenthesisToken, _
        getStrNameToken("x1"), getAsToken, getStrNameToken("int"), _
        getRightParenthesisToken)

    ' 2. 执行测试方法
    Set elem = analyzer.parseCastFunc(scanner, Nothing)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterFuncCol
    Debug.Assert scanner.hasNext = False
    Debug.Assert elem.toString(config) = "cast(x1 as int)"

    ' ==============================
    ' 6. 扫描器内: cast + ( + STR_NAME + AS + STR_NAME + ) + )
    '    左括号栈: 一个左括号
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getCastToken, _
        getLeftParenthesisToken, _
        getStrNameToken("x1"), getAsToken, getStrNameToken("int"), _
        getRightParenthesisToken, getRightParenthesisToken)
    Set stack = New ObjStack
    stack.push getLeftParenthesisToken

    ' 2. 执行测试方法
    Set elem = analyzer.parseCastFunc(scanner, stack)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterFuncCol
    Debug.Assert scanner.hasNext = False
    Debug.Assert elem.toString(config) = "(cast(x1 as int))"

End Sub

' 测试: 匹配cast函数的函数体
Private Sub test_parseCastFuncBody()
    Dim analyzer As SqlFormatterAnalyzer
    Set analyzer = createSqlFormatterAnalyzer

    Dim config As SqlFormatterConfiguration
    Set config = analyzer.getConfig

    Dim token As SqlToken
    Dim scanner As ObjArrayRefScanner
    Dim elem As ISqlFormatterElem
    Dim strArr As IPrimitiveArrayRef

    ' ==============================
    ' 1. 扫描器内: (
    ' ==============================
    ' 1. 创建扫描器
    Set token = getLeftParenthesisToken
    Set scanner = getObjArrayRefScanner(token)
    ' 2. 执行测试方法
    Set elem = analyzer.parseInOpParam(scanner)
    ' 3. 检查结果
    Debug.Assert elem Is token
    Debug.Assert scanner.hasNext = False

    ' ==============================
    ' 2. 扫描器内: ( + 一个注释
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getLeftParenthesisToken, createShareLineCommentToken("--c1"))
    ' 2. 执行测试方法
    Set elem = analyzer.parseCastFuncBody(scanner)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterContainerCol
    Debug.Assert scanner.hasNext = False
    Debug.Assert elem.toString(config) = "( --c1"

    ' ==============================
    ' 3. 扫描器内: ( + 一个注释 + STR_NAME
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getLeftParenthesisToken, _
        createShareLineCommentToken("--c1"), getStrNameToken("x1"))
    ' 2. 执行测试方法
    Set elem = analyzer.parseCastFuncBody(scanner)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterContainerCol
    Debug.Assert scanner.hasNext = False

    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 2
    Debug.Assert strArr.Item(0) = "( --c1"
    Debug.Assert strArr.Item(1) = "    x1"

    ' ==============================
    ' 4. 扫描器内: ( + 一个注释 + STR_NAME + UNION
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getLeftParenthesisToken, _
        createShareLineCommentToken("--c1"), getStrNameToken("x1"), _
        getUnionToken)
    ' 2. 执行测试方法
    Set elem = analyzer.parseCastFuncBody(scanner)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterContainerCol
    Debug.Assert scanner.hasNext = True
    Set token = scanner.getNext
    Debug.Assert token.tokenType = SqlTokenType.KEY_UNION

    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 2
    Debug.Assert strArr.Item(0) = "( --c1"
    Debug.Assert strArr.Item(1) = "    x1"

    ' ==============================
    ' 5. 扫描器内: ( + 一个注释 + STR_NAME + 一个注释 + AS
    '           + UNION(强制停止)
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getLeftParenthesisToken, _
        createShareLineCommentToken("--c1"), getStrNameToken("x1"), _
        createShareLineCommentToken("--c2"), getAsToken, _
        getUnionToken)
    ' 2. 执行测试方法
    Set elem = analyzer.parseCastFuncBody(scanner)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterContainerCol
    Debug.Assert scanner.hasNext = True
    Set token = scanner.getNext
    Debug.Assert token.tokenType = SqlTokenType.KEY_UNION

    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 3
    Debug.Assert strArr.Item(0) = "( --c1"
    Debug.Assert strArr.Item(1) = "    x1 --c2"
    Debug.Assert strArr.Item(2) = "    as"

    ' ==============================
    ' 6. 扫描器内: ( + 一个注释 + STR_NAME + 一个注释 + AS + 一个注释 + STR_NAME
    '           + UNION(强制停止)
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getLeftParenthesisToken, _
        createShareLineCommentToken("--c1"), getStrNameToken("x1"), _
        createShareLineCommentToken("--c2"), getAsToken, _
        createShareLineCommentToken("--c3"), getStrNameToken("int"), _
        getUnionToken)
    ' 2. 执行测试方法
    Set elem = analyzer.parseCastFuncBody(scanner)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterContainerCol
    Debug.Assert scanner.hasNext = True
    Set token = scanner.getNext
    Debug.Assert token.tokenType = SqlTokenType.KEY_UNION

    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 4
    Debug.Assert strArr.Item(0) = "( --c1"
    Debug.Assert strArr.Item(1) = "    x1 --c2"
    Debug.Assert strArr.Item(2) = "    as --c3"
    Debug.Assert strArr.Item(3) = "    int"

    ' ==============================
    ' 7. 扫描器内: ( + 一个注释 + STR_NAME + 一个注释 + AS + 一个注释 + STR_NAME
    '           + 一个注释 + )
    '           + UNION(强制停止)
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getLeftParenthesisToken, _
        createShareLineCommentToken("--c1"), getStrNameToken("x1"), _
        createShareLineCommentToken("--c2"), getAsToken, _
        createShareLineCommentToken("--c3"), getStrNameToken("int"), _
        createShareLineCommentToken("--c4"), getRightParenthesisToken, _
        getUnionToken)
    ' 2. 执行测试方法
    Set elem = analyzer.parseCastFuncBody(scanner)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterContainerCol
    Debug.Assert scanner.hasNext = True
    Set token = scanner.getNext
    Debug.Assert token.tokenType = SqlTokenType.KEY_UNION

    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 5
    Debug.Assert strArr.Item(0) = "( --c1"
    Debug.Assert strArr.Item(1) = "    x1 --c2"
    Debug.Assert strArr.Item(2) = "    as --c3"
    Debug.Assert strArr.Item(3) = "    int --c4"
    Debug.Assert strArr.Item(4) = ")"

    ' ==============================
    ' 8. 扫描器内: ( + STR_NAME + AS + STR_NAME + )
    '           + UNION(强制停止)
    ' 单行测试
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getLeftParenthesisToken, _
        getStrNameToken("x1"), getAsToken, getStrNameToken("int"), _
        getRightParenthesisToken, getUnionToken)
    ' 2. 执行测试方法
    Set elem = analyzer.parseCastFuncBody(scanner)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterContainerCol
    Debug.Assert scanner.hasNext = True
    Set token = scanner.getNext
    Debug.Assert token.tokenType = SqlTokenType.KEY_UNION

    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 1
    Debug.Assert strArr.Item(0) = "(x1 as int)"

End Sub

' 测试: 匹配一个单独的列对象
Private Sub test_parseSingleCol()
    Dim analyzer As SqlFormatterAnalyzer
    Set analyzer = createSqlFormatterAnalyzer

    Dim config As SqlFormatterConfiguration
    Set config = analyzer.getConfig

    Dim token As SqlToken
    Dim scanner As ObjArrayRefScanner
    Dim elem As ISqlFormatterElem
    Dim strArr As IPrimitiveArrayRef

    ' ==============================
    ' 1. 扫描器内: (
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getLeftParenthesisToken)
    ' 2. 执行测试方法
    Set elem = analyzer.parseSingleCol(scanner, False)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterContainerCol
    Debug.Assert scanner.hasNext = False
    Debug.Assert elem.toString(config) = "("

    ' ==============================
    ' 2. 扫描器内: ( + 一个注释
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getLeftParenthesisToken, createShareLineCommentToken("--c1"))
    ' 2. 执行测试方法
    Set elem = analyzer.parseSingleCol(scanner, False)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterContainerCol
    Debug.Assert scanner.hasNext = False
    Debug.Assert elem.toString(config) = "( --c1"

    ' ==============================
    ' 3. 扫描器内: ( + 一个注释 +  not
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner( _
        getLeftParenthesisToken, _
        createShareLineCommentToken("--c1"), _
        getNotToken)
    ' 2. 执行测试方法
    Set elem = analyzer.parseSingleCol(scanner, False)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterContainerCol
    Debug.Assert scanner.hasNext = False
    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 2
    Debug.Assert strArr.Item(0) = "( --c1"
    Debug.Assert strArr.Item(1) = "    not"

    ' ==============================
    ' 4. 扫描器内: ( + 一个注释 + binary
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner( _
        getLeftParenthesisToken, _
        createShareLineCommentToken("--c1"), _
        getBinaryToken)
    ' 2. 执行测试方法
    Set elem = analyzer.parseSingleCol(scanner, False)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterContainerCol
    Debug.Assert scanner.hasNext = False
    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 2
    Debug.Assert strArr.Item(0) = "( --c1"
    Debug.Assert strArr.Item(1) = "    binary"

    ' ==============================
    ' 5. 扫描器内: ( + 一个注释 + not + STR_NAME
    '    测试: 处理开头存在 `NOT | BINARY` 的情况
    '     2. 调用: [匹配一个单独的列对象---parseSingleCol](#匹配一个单独的列对象---parseSingleCol)，解析结果保存到 `tempElem`
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner( _
        getLeftParenthesisToken, _
        createShareLineCommentToken("--c1"), _
        getNotToken, _
        getStrNameToken("x1"))
    ' 2. 执行测试方法
    Set elem = analyzer.parseSingleCol(scanner, False)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterContainerCol
    Debug.Assert scanner.hasNext = False
    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 2
    Debug.Assert strArr.Item(0) = "( --c1"
    Debug.Assert strArr.Item(1) = "    not x1"

    ' ==============================
    ' 6. 扫描器内: ( + 一个注释 + STR_NAME
    '    测试: 处理开头不存在 `NOT | BINARY` 的情况
    '         1. [匹配单列名称表达式--parseColExpr](#匹配单列名称表达式--parseColExpr)，结果保存到 `result`
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner( _
        getLeftParenthesisToken, _
        createShareLineCommentToken("--c1"), _
        getStrNameToken("x1"))
    ' 2. 执行测试方法
    Set elem = analyzer.parseSingleCol(scanner, False)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterContainerCol
    Debug.Assert scanner.hasNext = False
    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 2
    Debug.Assert strArr.Item(0) = "( --c1"
    Debug.Assert strArr.Item(1) = "    x1"

    ' ==============================
    ' 8. 扫描器内: STR_NAME
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getStrNameToken("x1"))
    ' 2. 执行测试方法
    Set elem = analyzer.parseSingleCol(scanner, False)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlToken
    Debug.Assert scanner.hasNext = False
    Debug.Assert elem.toString(config) = "x1"

    ' ==============================
    ' 9. 扫描器内: ( + 一个注释 + ( +  STR_NAME + )
    '    测试: 结束前吸收括号
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner( _
        getLeftParenthesisToken, _
        createShareLineCommentToken("--c1"), _
        getLeftParenthesisToken, _
        getStrNameToken("x1"), _
        getRightParenthesisToken)
    ' 2. 执行测试方法
    Set elem = analyzer.parseSingleCol(scanner, False)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterContainerCol
    Debug.Assert scanner.hasNext = False
    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 2
    Debug.Assert strArr.Item(0) = "( --c1"
    Debug.Assert strArr.Item(1) = "    (x1)"

    ' ==============================
    ' 10. 扫描器内: ( + STR_NAME + [+] + STR_NAME + )
    '    测试: 从运算符开始匹配表达式
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner( _
        getLeftParenthesisToken, _
        getStrNameToken("x1"), _
        getPlusToken, _
        getStrNameToken("x2"), _
        getRightParenthesisToken)
    ' 2. 执行测试方法
    Set elem = analyzer.parseSingleCol(scanner, False)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterExprCol
    Debug.Assert scanner.hasNext = False
    Debug.Assert elem.toString(config) = "(x1 + x2)"

    ' ==============================
    ' 11. 扫描器内: ( + STR_NAME + [+] + STR_NAME + )
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner( _
        getLeftParenthesisToken, _
        getStrNameToken("x1"), _
        getPlusToken, _
        getStrNameToken("x2"), _
        getRightParenthesisToken)
    ' 2. 执行测试方法
    Set elem = analyzer.parseSingleCol(scanner, True)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterExprCol
    Debug.Assert scanner.hasNext = False
    Debug.Assert elem.toString(config) = "(x1 + x2)"

    ' ==============================
    ' 12. 扫描器内: ( + 一个注释 + (
    '                 + Exists
    '                 + (
    '                       + select + STR_NAME
    '                       + from + STR_NAME
    '                 + )
    '            + ) + 一个注释 + )
    '            + <=
    '    测试: 5. 处理开头不存在 `NOT | BINARY` 的情况 (匹配一个单独的列对象)
    '             3. 检查: 如果 `tokenType` 是 `SqlTokenType.KEY_EXISTS`
    '         并且能够吸收边界括号 + 遇到条件运算符 <= 时停止: needStopInFirstNonArithmetic = True
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner( _
        getLeftParenthesisToken, createShareLineCommentToken("--c1"), getLeftParenthesisToken, _
        _
        getExistsToken, _
        _
        getLeftParenthesisToken, _
        _
        getSelectToken, getStrNameToken("x1"), _
        getFromToken, getStrNameToken("x2"), _
        _
        getRightParenthesisToken, _
        getRightParenthesisToken, createShareLineCommentToken("--c2"), getRightParenthesisToken, _
        _
        getLeConditionOperator)

    ' 2. 执行测试
    Set elem = analyzer.parseSingleCol(scanner, True)
    ' 4. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterContainerCol
    Debug.Assert scanner.hasNext = True
    Set token = scanner.getNext
    Debug.Assert token.tokenType = SqlTokenType.OPERA_LE

    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 10
    Debug.Assert strArr.Item(0) = "( --c1"
    Debug.Assert strArr.Item(1) = "    ("
    Debug.Assert strArr.Item(2) = "        exists ("
    Debug.Assert strArr.Item(3) = "            select"
    Debug.Assert strArr.Item(4) = "                x1"
    Debug.Assert strArr.Item(5) = "            from"
    Debug.Assert strArr.Item(6) = "                x2"
    Debug.Assert strArr.Item(7) = "        )"
    Debug.Assert strArr.Item(8) = "    ) --c2"
    Debug.Assert strArr.Item(9) = ")"

    ' ==============================
    ' 13. 扫描器内:
    '            Exists
    '                 + (
    '                       + select + STR_NAME
    '                       + from + STR_NAME
    '                 + )
    '            + <=
    '    测试: 5. 处理开头不存在 `NOT | BINARY` 的情况 (匹配一个单独的列对象)
    '             3. 检查: 如果 `tokenType` 是 `SqlTokenType.KEY_EXISTS`
    '         并且能够吸收边界括号 + 遇到条件运算符 <= 时不停止: needStopInFirstNonArithmetic = True
    '                                              (这里仍然不会解析 <= 因为没有边界括号)
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner( _
        getExistsToken, _
        _
        getLeftParenthesisToken, _
        _
        getSelectToken, getStrNameToken("x1"), _
        getFromToken, getStrNameToken("x2"), _
        _
        getRightParenthesisToken, _
        _
        getLeConditionOperator)

    ' 2. 执行测试
    Set elem = analyzer.parseSingleCol(scanner, False)
    ' 4. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterTwoElemCondition
    Debug.Assert scanner.hasNext = True
    Set token = scanner.getNext
    Debug.Assert token.tokenType = SqlTokenType.OPERA_LE

    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 6
    Debug.Assert strArr.Item(0) = "exists ("
    Debug.Assert strArr.Item(1) = "    select"
    Debug.Assert strArr.Item(2) = "        x1"
    Debug.Assert strArr.Item(3) = "    from"
    Debug.Assert strArr.Item(4) = "        x2"
    Debug.Assert strArr.Item(5) = ")"

    ' ==============================
    ' 14. 扫描器内: (
    '             + select + STR_NAME
    '             + from
    '                       + (
    '                       + select + STR_NAME
    '                       + from + STR_NAME
    '                       + )
    '             + where
    '                   + STR_NAME + <= + STR_NAME
    '                   + and + not + exists + (
    '                       + select + STR_NAME
    '                       + from + STR_NAME
    '                   + )
    '             + )
    '             + As + STR_NAME
    '             + ( + STR_NAME + [,] + STR_NAME + [,] + STR_NAME + )
    '    测试: 匹配到一个 select 开头的子查询
    '         + 子查询中也包含子查询 + 查询条件中也包含子查询
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getLeftParenthesisToken, _
        _
        getSelectToken, getStrNameToken("a2"), _
        getFromToken, _
        _
        getLeftParenthesisToken, _
        getSelectToken, getStrNameToken("b1"), _
        getFromToken, getStrNameToken("table2"), _
        getRightParenthesisToken, _
        _
        getWhereToken, getStrNameToken("q1"), getLeConditionOperator, getStrNameToken("q2"), _
        _
        getAndToken, getNotToken, getExistsToken, getLeftParenthesisToken, _
        getSelectToken, getStrNameToken("e1"), getFromToken, getStrNameToken("subTable"), _
        getRightParenthesisToken, _
        _
        getAndToken, getStrNameToken("q3"), getLeConditionOperator, getStrNameToken("q4"), _
        _
        getRightParenthesisToken)
    ' 2. 执行测试方法
    Set elem = analyzer.parseSingleCol(scanner, False)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterContainerEnv
    Debug.Assert scanner.hasNext = False

    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 20
    Debug.Assert strArr.Item(0)  = "("
    Debug.Assert strArr.Item(1)  = "    select"
    Debug.Assert strArr.Item(2)  = "        a2"
    Debug.Assert strArr.Item(3)  = "    from"
    Debug.Assert strArr.Item(4)  = "        ("
    Debug.Assert strArr.Item(5)  = "            select"
    Debug.Assert strArr.Item(6)  = "                b1"
    Debug.Assert strArr.Item(7)  = "            from"
    Debug.Assert strArr.Item(8)  = "                table2"
    Debug.Assert strArr.Item(9)  = "        )"
    Debug.Assert strArr.Item(10) = "    where"
    Debug.Assert strArr.Item(11) = "        q1 <= q2"
    Debug.Assert strArr.Item(12) = "        and not exists ("
    Debug.Assert strArr.Item(13) = "                select"
    Debug.Assert strArr.Item(14) = "                    e1"
    Debug.Assert strArr.Item(15) = "                from"
    Debug.Assert strArr.Item(16) = "                    subTable"
    Debug.Assert strArr.Item(17) = "            )"
    Debug.Assert strArr.Item(18) = "        and q3 <= q4"
    Debug.Assert strArr.Item(19) = ")"

    ' ==============================
    ' 15. 扫描器内: (
    '             + with + STR_FUNC
    '                         + ( + STR_NAME + , + STR_NAME+ )
    '                         + As + (
    '                              +  select + STR_NAME
    '                              +  FROm + STR_NAME
    '                         + )
    '             + select + STR_NAME
    '             + from
    '                       + (
    '                       + select + STR_NAME
    '                       + from + STR_NAME
    '                       + )
    '             + where
    '                   + STR_NAME + <= + STR_NAME
    '                   + and + not + exists + (
    '                       + select + STR_NAME
    '                       + from + STR_NAME
    '                   + )
    '             + )
    '             +  As + STR_NAME
    '             + ( + STR_NAME + [,] + STR_NAME + [,] + STR_NAME + )
    '    测试: 匹配到一个 with 开头的子查询
    '         + 子查询中也包含子查询 + 查询条件中也包含子查询
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getLeftParenthesisToken, _
        getWithToken, getStrNameToken("wt"), _
        getLeftParenthesisToken, getStrNameToken("c1"), getCommaToken, getStrNameToken("c2"), getRightParenthesisToken, _
        getAsToken, getLeftParenthesisToken, _
        getSelectToken, getStrNameToken("ws1"), _
        getFromToken, getStrNameToken("ws2"), _
        getRightParenthesisToken, _
        _
        getSelectToken, getStrNameToken("a2"), _
        getFromToken, _
        _
        getLeftParenthesisToken, _
        getSelectToken, getStrNameToken("b1"), _
        getFromToken, getStrNameToken("table2"), _
        getRightParenthesisToken, _
        _
        getWhereToken, getStrNameToken("q1"), getLeConditionOperator, getStrNameToken("q2"), _
        _
        getAndToken, getNotToken, getExistsToken, getLeftParenthesisToken, _
        getSelectToken, getStrNameToken("e1"), getFromToken, getStrNameToken("subTable"), _
        getRightParenthesisToken, _
        _
        getAndToken, getStrNameToken("q3"), getLeConditionOperator, getStrNameToken("q4"), _
        _
        getRightParenthesisToken)
    ' 2. 执行测试方法
    Set elem = analyzer.parseSingleCol(scanner, False)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterContainerEnv
    Debug.Assert scanner.hasNext = False

    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 26
    Debug.Assert strArr.Item(0)  = "("
    Debug.Assert strArr.Item(1)  = "    with wt (c1, c2) as ("
    Debug.Assert strArr.Item(2)  = "        select"
    Debug.Assert strArr.Item(3)  = "            ws1"
    Debug.Assert strArr.Item(4)  = "        from"
    Debug.Assert strArr.Item(5)  = "            ws2"
    Debug.Assert strArr.Item(6)  = "    )"
    Debug.Assert strArr.Item(7)  = "    select"
    Debug.Assert strArr.Item(8)  = "        a2"
    Debug.Assert strArr.Item(9)  = "    from"
    Debug.Assert strArr.Item(10) = "        ("
    Debug.Assert strArr.Item(11) = "            select"
    Debug.Assert strArr.Item(12) = "                b1"
    Debug.Assert strArr.Item(13) = "            from"
    Debug.Assert strArr.Item(14) = "                table2"
    Debug.Assert strArr.Item(15) = "        )"
    Debug.Assert strArr.Item(16) = "    where"
    Debug.Assert strArr.Item(17) = "        q1 <= q2"
    Debug.Assert strArr.Item(18) = "        and not exists ("
    Debug.Assert strArr.Item(19) = "                select"
    Debug.Assert strArr.Item(20) = "                    e1"
    Debug.Assert strArr.Item(21) = "                from"
    Debug.Assert strArr.Item(22) = "                    subTable"
    Debug.Assert strArr.Item(23) = "            )"
    Debug.Assert strArr.Item(24) = "        and q3 <= q4"
    Debug.Assert strArr.Item(25) = ")"
End Sub

' 测试: 从运算符开始匹配表达式
Private Sub test_parseExprColStartWithOperator()
    Dim analyzer As SqlFormatterAnalyzer
    Set analyzer = createSqlFormatterAnalyzer

    Dim config As SqlFormatterConfiguration
    Set config = analyzer.getConfig

    Dim token As SqlToken
    Dim scanner As ObjArrayRefScanner
    Dim elem As ISqlFormatterElem
    Dim strArr As IPrimitiveArrayRef
    Dim stack As ObjStack

    ' ==============================
    ' 1. 扫描器内: [+]
    '    测试运算符
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getPlusToken)
    ' 2. 执行测试方法
    Set elem = analyzer.parseExprColStartWithOperator(getStrNameToken("y1"), Nothing, scanner, False)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterExprCol
    Debug.Assert scanner.hasNext = False
    Debug.Assert elem.toString(config) = "y1 +"

    ' ==============================
    ' 2. 扫描器内: [+] + STR_NAME
    '    测试运算符
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getPlusToken, getStrNameToken("x1"))
    ' 2. 执行测试方法
    Set elem = analyzer.parseExprColStartWithOperator(getStrNameToken("y1"), Nothing, scanner, False)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterExprCol
    Debug.Assert scanner.hasNext = False
    Debug.Assert elem.toString(config) = "y1 + x1"

    ' ==============================
    ' 3. 扫描器内: [+] + STR_NAME + [+]
    '    测试运算符
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getPlusToken, getStrNameToken("x1"), getPlusToken)
    ' 2. 执行测试方法
    Set elem = analyzer.parseExprColStartWithOperator(getStrNameToken("y1"), Nothing, scanner, False)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterExprCol
    Debug.Assert scanner.hasNext = False
    Debug.Assert elem.toString(config) = "y1 + x1 +"

    ' ==============================
    ' 4. 扫描器内: [+] + STR_NAME + [+] + STR_NAME
    '    测试运算符
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getPlusToken, getStrNameToken("x1"), _
        getPlusToken, getStrNameToken("x2"))
    ' 2. 执行测试方法
    Set elem = analyzer.parseExprColStartWithOperator(getStrNameToken("y1"), Nothing, scanner, False)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterExprCol
    Debug.Assert scanner.hasNext = False
    Debug.Assert elem.toString(config) = "y1 + x1 + x2"

    ' ==============================
    ' 5. 扫描器内: [+] + STR_NAME + [+] + STR_NAME + )
    '            参数需要增加一个左括号到括号栈
    '    测试运算符
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getPlusToken, getStrNameToken("x1"), _
        getPlusToken, getStrNameToken("x2"), getRightParenthesisToken)
    Set stack = New ObjStack
    stack.push getLeftParenthesisToken

    ' 2. 执行测试方法
    Set elem = analyzer.parseExprColStartWithOperator(getStrNameToken("y1"), stack, scanner, False)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterExprCol
    Debug.Assert scanner.hasNext = False
    Debug.Assert elem.toString(config) = "(y1 + x1 + x2)"

    ' ==============================
    ' 6. 扫描器内: [+] + STR_NAME + [+] + STR_NAME + )
    '           + and + STR_NAME + <= STR_NAME
    '            参数需要增加一个左括号到括号栈
    '    测试运算符 + 逻辑运算符
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner( _
        getPlusToken, getStrNameToken("x1"), _
        getPlusToken, getStrNameToken("x2"), getRightParenthesisToken, _
        _
        getAndToken, getStrNameToken("z1"), getLeConditionOperator, getStrNameToken("z2"))
    Set stack = New ObjStack
    stack.push getLeftParenthesisToken

    ' 2. 执行测试方法
    Set elem = analyzer.parseExprColStartWithOperator(getStrNameToken("y1"), stack, scanner, False)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterMultiConditions
    Debug.Assert scanner.hasNext = False
    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 2
    Debug.Assert strArr.Item(0) = "(y1 + x1 + x2)"
    Debug.Assert strArr.Item(1) = "and z1 <= z2"

    ' ==============================
    ' 7. 扫描器内: [+] + STR_NAME + [+] + STR_NAME + )
    '           + and + STR_NAME + <= STR_NAME
    '           + and + STR_NAME + <= STR_NAME
    '            参数需要增加一个左括号到括号栈
    '    测试运算符 + 逻辑运算符--连续出现两次
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner( _
        getPlusToken, getStrNameToken("x1"), _
        getPlusToken, getStrNameToken("x2"), getRightParenthesisToken, _
        _
        getAndToken, getStrNameToken("z1"), getLeConditionOperator, getStrNameToken("z2"), _
        getAndToken, getStrNameToken("a1"), getLeConditionOperator, getStrNameToken("a2"))
    Set stack = New ObjStack
    stack.push getLeftParenthesisToken

    ' 2. 执行测试方法
    Set elem = analyzer.parseExprColStartWithOperator(getStrNameToken("y1"), stack, scanner, False)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterMultiConditions
    Debug.Assert scanner.hasNext = False
    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 3
    Debug.Assert strArr.Item(0) = "(y1 + x1 + x2)"
    Debug.Assert strArr.Item(1) = "and z1 <= z2"
    Debug.Assert strArr.Item(2) = "and a1 <= a2"

    ' ==============================
    ' 8. 扫描器内: [+] + STR_NAME + [+] + STR_NAME + )
    '           + and + STR_NAME + <= STR_NAME
    '            参数需要增加一个左括号到括号栈
    '    测试运算符 + 逻辑运算符 + 参数=遇到逻辑运算符时停止解析
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner( _
        getPlusToken, getStrNameToken("x1"), _
        getPlusToken, getStrNameToken("x2"), getRightParenthesisToken, _
        _
        getAndToken, getStrNameToken("z1"), getLeConditionOperator, getStrNameToken("z2"))
    Set stack = New ObjStack
    stack.push getLeftParenthesisToken

    ' 2. 执行测试方法
    Set elem = analyzer.parseExprColStartWithOperator(getStrNameToken("y1"), stack, scanner, True)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterExprCol
    Debug.Assert scanner.hasNext = True
    Set token = scanner.getNext
    Debug.Assert token.tokenType = SqlTokenType.KEY_AND
    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 1
    Debug.Assert strArr.Item(0) = "(y1 + x1 + x2)"

    ' ==============================
    ' 9. 扫描器内: [+] + STR_NAME + [+] + STR_NAME + )
    '           + and + STR_NAME + <= STR_NAME + )
    '           + Or + STR_NAME + <= STR_NAME (不会解析这一行内容)
    '            参数需要增加【两个】左括号到括号栈
    '    测试运算符 + 逻辑运算符 + 参数=遇到逻辑运算符时停止解析 + 边界括号内的逻辑运算符仍然会被解析
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner( _
        getPlusToken, getStrNameToken("x1"), _
        getPlusToken, getStrNameToken("x2"), getRightParenthesisToken, _
        _
        getAndToken, getStrNameToken("z1"), getLeConditionOperator, getStrNameToken("z2"), _
        getRightParenthesisToken, _
        _
        getOrToken, getStrNameToken("a1"), getLeConditionOperator, getStrNameToken("a2"))
    Set stack = New ObjStack
    stack.push getLeftParenthesisToken
    stack.push getLeftParenthesisToken

    ' 2. 执行测试方法
    Set elem = analyzer.parseExprColStartWithOperator(getStrNameToken("y1"), stack, scanner, True)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterMultiConditions
    Debug.Assert scanner.hasNext = True
    Set token = scanner.getNext
    Debug.Assert token.tokenType = SqlTokenType.KEY_OR

    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 4
    Debug.Assert strArr.Item(0) = "("
    Debug.Assert strArr.Item(1) = "    (y1 + x1 + x2)"
    Debug.Assert strArr.Item(2) = "    and z1 <= z2"
    Debug.Assert strArr.Item(3) = ")"

    ' ==============================
    ' 10. 扫描器内: [+] + STR_NAME + [+] + STR_NAME + )
    '                 + <= + STR_NAME
    '           + and + STR_NAME + <= STR_NAME
    '            参数需要增加【两个】左括号到括号栈
    '    测试运算符 + 条件运算符 + 逻辑运算符
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner( _
        getPlusToken, getStrNameToken("x1"), _
        getPlusToken, getStrNameToken("x2"), getRightParenthesisToken, _
        _
        getLeConditionOperator, getStrNameToken("a1"), _
        _
        getAndToken, getStrNameToken("z1"), getLeConditionOperator, getStrNameToken("z2"))
    Set stack = New ObjStack
    stack.push getLeftParenthesisToken

    ' 2. 执行测试方法
    Set elem = analyzer.parseExprColStartWithOperator(getStrNameToken("y1"), stack, scanner, False)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterMultiConditions
    Debug.Assert scanner.hasNext = False

    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 2
    Debug.Assert strArr.Item(0) = "(y1 + x1 + x2) <= a1"
    Debug.Assert strArr.Item(1) = "and z1 <= z2"

    ' ==============================
    ' 11. 扫描器内: [+] + STR_NAME + [+] + STR_NAME + )
    '                 + Not + <= + STR_NAME
    '           + and + STR_NAME + <= STR_NAME
    '            参数需要增加【两个】左括号到括号栈
    '    测试运算符 + 条件运算符(Not 开头) + 逻辑运算符
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner( _
        getPlusToken, getStrNameToken("x1"), _
        getPlusToken, getStrNameToken("x2"), getRightParenthesisToken, _
        _
        getNotToken, getLeConditionOperator, getStrNameToken("a1"), _
        _
        getAndToken, getStrNameToken("z1"), getLeConditionOperator, getStrNameToken("z2"))
    Set stack = New ObjStack
    stack.push getLeftParenthesisToken

    ' 2. 执行测试方法
    Set elem = analyzer.parseExprColStartWithOperator(getStrNameToken("y1"), stack, scanner, False)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterMultiConditions
    Debug.Assert scanner.hasNext = False

    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 2
    Debug.Assert strArr.Item(0) = "(y1 + x1 + x2) not <= a1"
    Debug.Assert strArr.Item(1) = "and z1 <= z2"

    ' ==============================
    ' 12. 扫描器内: [+] + STR_NAME
    '    测试起始元素 startElem = null + 算数运算符
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getPlusToken, getStrNameToken("x1"))

    ' 2. 执行测试方法
    Set elem = analyzer.parseExprColStartWithOperator(Nothing, Nothing, scanner, False)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterExprCol
    Debug.Assert scanner.hasNext = False

    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 1
    Debug.Assert strArr.Item(0) = "+ x1"

    ' ==============================
    ' 13. 扫描器内: <= + STR_NAME
    '    测试起始元素 startElem = null + 条件运算符
    '    没有左侧列元素，所以前面没有对齐空格
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getLeConditionOperator, getStrNameToken("x1"))

    ' 2. 执行测试方法
    Set elem = analyzer.parseExprColStartWithOperator(Nothing, Nothing, scanner, False)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterTwoElemCondition
    Debug.Assert scanner.hasNext = False

    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 1
    Debug.Assert strArr.Item(0) = "<= x1"

    ' ==============================
    ' 14. 扫描器内: and + STR_NAME
    '    测试起始元素 startElem = null + 逻辑运算符
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getAndToken, getStrNameToken("x1"))

    ' 2. 执行测试方法
    Set elem = analyzer.parseExprColStartWithOperator(Nothing, Nothing, scanner, False)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterMultiConditions
    Debug.Assert scanner.hasNext = False

    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 1
    Debug.Assert strArr.Item(0) = "and x1"

    ' ==============================
    ' 14. 扫描器内: 一个注释 + [+] + STR_NAME
    '    测试起始元素 startElem = null 运算符前的注释可以被吸收 + 算数运算符
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(createShareLineCommentToken("--c1"), getPlusToken, getStrNameToken("x1"))

    ' 2. 执行测试方法
    Set elem = analyzer.parseExprColStartWithOperator(Nothing, Nothing, scanner, False)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterExprCol
    Debug.Assert scanner.hasNext = False

    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 2
    Debug.Assert strArr.Item(0) = "--c1"
    Debug.Assert strArr.Item(1) = "    + x1"

    ' ==============================
    ' 15. 扫描器内: 一个注释 + [+] + STR_NAME + 一个单行注释 + [and] + STR_NAME
    '    测试起始元素 startElem = null 运算符前的注释可以被吸收 + 连续解析多个运算符
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(createShareLineCommentToken("--c1"), _
        getPlusToken, getStrNameToken("x1"), _
        createSingleLineCommentToken("--c2"), getAndToken, getStrNameToken("x2"))

    ' 2. 执行测试方法
    Set elem = analyzer.parseExprColStartWithOperator(Nothing, Nothing, scanner, False)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterMultiConditions
    Debug.Assert scanner.hasNext = False

    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 4
    Debug.Assert strArr.Item(0) = "--c1"
    Debug.Assert strArr.Item(1) = "    + x1"
    Debug.Assert strArr.Item(2) = "    --c2"
    Debug.Assert strArr.Item(3) = "and x2"

    ' ==============================
    ' 16. 扫描器内: 一个注释 + [+] + STR_NAME + 一个单行注释 + [and] + STR_NAME
    '    测试起始元素 startElem = null 运算符前的注释可以被吸收
    '    遇到非算数运算符时停止，并且算数运算符前面的注释不会吸收，但是前面元素后的同行注释会被吸收
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(createShareLineCommentToken("--c1"), _
        getPlusToken, getStrNameToken("x1"), createShareLineCommentToken("--c2"), _
        createSingleLineCommentToken("--c3"), getAndToken, getStrNameToken("x2"))

    ' 2. 执行测试方法
    Set elem = analyzer.parseExprColStartWithOperator(Nothing, Nothing, scanner, True)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterExprCol
    Debug.Assert scanner.hasNext = True
    Set token = scanner.getNext
    Debug.Assert token.tokenType = SqlTokenType.SINGLE_LINE_COMMENT
    Debug.Assert token.Value = "--c3"

    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 2
    Debug.Assert strArr.Item(0) = "--c1"
    Debug.Assert strArr.Item(1) = "    + x1 --c2"
End Sub

' 测试: 匹配单列名称表达式
Private Sub test_parseColExpr()
    Dim analyzer As SqlFormatterAnalyzer
    Set analyzer = createSqlFormatterAnalyzer

    Dim config As SqlFormatterConfiguration
    Set config = analyzer.getConfig

    Dim token As SqlToken
    Dim scanner As ObjArrayRefScanner
    Dim elem As ISqlFormatterElem
    Dim strArr As IPrimitiveArrayRef
    Dim stack As ObjStack

    ' ==============================
    ' 1. 扫描器内: )
    '    左括号栈: 一个左括号
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getRightParenthesisToken)
    Set stack = New ObjStack
    stack.push getLeftParenthesisToken
    ' 2. 执行测试方法
    Set elem = analyzer.parseColExpr(scanner, stack)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterContainerCol
    Debug.Assert scanner.hasNext = False
    Debug.Assert elem.toString(config) = "()"

    ' ==============================
    ' 2. 扫描器内: STR_NAME
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getStrNameToken("x1"))
    ' 2. 执行测试方法
    Set elem = analyzer.parseColExpr(scanner, Nothing)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlToken
    Debug.Assert scanner.hasNext = False
    Debug.Assert elem.toString(config) = "x1"

    ' ==============================
    ' 3. 扫描器内: STR_NAME + )
    '    左括号栈: 一个左括号
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getStrNameToken("x1"), getRightParenthesisToken)
    Set stack = New ObjStack
    stack.push getLeftParenthesisToken
    ' 2. 执行测试方法
    Set elem = analyzer.parseColExpr(scanner, stack)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterContainerCol
    Debug.Assert scanner.hasNext = False
    Debug.Assert elem.toString(config) = "(x1)"

    ' ==============================
    ' 3. 扫描器内: STR_NAME + [.] + STR_NAME
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner( _
        getStrNameToken("table1"), getPointToken, getStrNameToken("x1"))
    ' 2. 执行测试方法
    Set elem = analyzer.parseColExpr(scanner, Nothing)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterContinuousStrElem
    Debug.Assert scanner.hasNext = False
    Debug.Assert elem.toString(config) = "table1.x1"

    ' ==============================
    ' 4. 扫描器内: [*]
    ' ==============================
    ' 1. 创建扫描器
    Set token = getAsteriskToken
    Set scanner = getObjArrayRefScanner(token)
    ' 2. 执行测试方法
    Set elem = analyzer.parseColExpr(scanner, Nothing)
    ' 3. 检查结果
    Debug.Assert elem Is token
    Debug.Assert scanner.hasNext = False

    ' ==============================
    ' 5. 扫描器内: [123]
    ' ==============================
    ' 1. 创建扫描器
    Set token = createNumToken("123")
    Set scanner = getObjArrayRefScanner(token)
    ' 2. 执行测试方法
    Set elem = analyzer.parseColExpr(scanner, Nothing)
    ' 3. 检查结果
    Debug.Assert elem Is token
    Debug.Assert scanner.hasNext = False

    ' ==============================
    ' 6. 扫描器内: ["123xyz"]
    ' ==============================
    ' 1. 创建扫描器
    Set token = createStrToken("123xyz")
    Set scanner = getObjArrayRefScanner(token)
    ' 2. 执行测试方法
    Set elem = analyzer.parseColExpr(scanner, Nothing)
    ' 3. 检查结果
    Debug.Assert elem Is token
    Debug.Assert scanner.hasNext = False

    ' ==============================
    ' 7. 扫描器内: True
    ' ==============================
    ' 1. 创建扫描器
    Set token = getTrueToken
    Set scanner = getObjArrayRefScanner(token)
    ' 2. 执行测试方法
    Set elem = analyzer.parseColExpr(scanner, Nothing)
    ' 3. 检查结果
    Debug.Assert elem Is token
    Debug.Assert scanner.hasNext = False

    ' ==============================
    ' 8. 扫描器内: False
    ' ==============================
    ' 1. 创建扫描器
    Set token = getFalseToken
    Set scanner = getObjArrayRefScanner(token)
    ' 2. 执行测试方法
    Set elem = analyzer.parseColExpr(scanner, Nothing)
    ' 3. 检查结果
    Debug.Assert elem Is token
    Debug.Assert scanner.hasNext = False

    ' ==============================
    ' 9. 扫描器内: null
    ' ==============================
    ' 1. 创建扫描器
    Set token = getNullToken
    Set scanner = getObjArrayRefScanner(token)
    ' 2. 执行测试方法
    Set elem = analyzer.parseColExpr(scanner, Nothing)
    ' 3. 检查结果
    Debug.Assert elem Is token
    Debug.Assert scanner.hasNext = False

    ' ==============================
    ' 10. 扫描器内: 时间戳 CURRENT_TIMESTAMP
    ' ==============================
    ' 1. 创建扫描器
    Set token = getCurrentTimestampToken
    Set scanner = getObjArrayRefScanner(token)
    ' 2. 执行测试方法
    Set elem = analyzer.parseColExpr(scanner, Nothing)
    ' 3. 检查结果
    Debug.Assert elem Is token
    Debug.Assert scanner.hasNext = False

    ' ==============================
    ' 11. 扫描器内: case
    '              + when + STR_NAME + Then + STR_NAME
    '              + end
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getCaseToken, _
        getWhenToken, getStrNameToken("x1"), getThenToken, getStrNameToken("x2"), _
        getEndToken)
    ' 2. 执行测试方法
    Set elem = analyzer.parseColExpr(scanner, Nothing)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterCaseCol
    Debug.Assert scanner.hasNext = False

    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 3
    Debug.Assert strArr.Item(0) = "case"
    Debug.Assert strArr.Item(1) = "    when x1 then x2"
    Debug.Assert strArr.Item(2) = "end"

    ' ==============================
    ' 12. 扫描器内: 一个函数名 + ( + STR_NAME + [,] + STR_NAME + ) + )
    '    左括号栈内: 一个左括号
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(createFuncNameToken("abc"), _
        getLeftParenthesisToken, _
        getStrNameToken("x1"), getCommaToken, getStrNameToken("x2"), _
        getRightParenthesisToken, getRightParenthesisToken)
    Set stack = New ObjStack
    stack.push getLeftParenthesisToken
    ' 2. 执行测试方法
    Set elem = analyzer.parseFunction(scanner, stack)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterFuncCol
    Debug.Assert scanner.hasNext = False
    Set strArr = elem.toStringArray(config)
    Debug.Assert elem.toString(config) = "(abc(x1, x2))"

    ' ==============================
    ' 13. 扫描器内: 一个函数名 + (
    '                 + STR_NAME
    '                 + [,]
    '                 + case
    '                       + when + STR_NAME + Then + STR_NAME
    '                       + end
    '                 + ) + )
    '    左括号栈内: 一个左括号
    '    多行测试
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(createFuncNameToken("abc"), getLeftParenthesisToken, _
        getStrNameToken("y1"), getCommaToken, _
        _
        getCaseToken, _
        getWhenToken, getStrNameToken("x1"), getThenToken, getStrNameToken("x2"), _
        getEndToken, _
        _
        getRightParenthesisToken, getRightParenthesisToken)
    Set stack = New ObjStack
    stack.push getLeftParenthesisToken
    ' 2. 执行测试方法
    Set elem = analyzer.parseFunction(scanner, stack)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterFuncCol
    Debug.Assert scanner.hasNext = False

    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 8
    Debug.Assert strArr.Item(0) = "("
    Debug.Assert strArr.Item(1) = "    abc("
    Debug.Assert strArr.Item(2) = "        y1,"
    Debug.Assert strArr.Item(3) = "        case"
    Debug.Assert strArr.Item(4) = "            when x1 then x2"
    Debug.Assert strArr.Item(5) = "        end"
    Debug.Assert strArr.Item(6) = "    )"
    Debug.Assert strArr.Item(7) = ")"

    ' ==============================
    ' 14. 扫描器内: [+]
    ' ==============================
    ' 1. 创建扫描器
    Set token = getPlusToken
    Set scanner = getObjArrayRefScanner(token)
    ' 2. 执行测试方法
    Set elem = analyzer.parseColExpr(scanner, Nothing)
    ' 3. 检查结果
    Debug.Assert elem Is token
    Debug.Assert scanner.hasNext = False

    ' ==============================
    ' 15. 扫描器内: [-]
    ' ==============================
    ' 1. 创建扫描器
    Set token = getMinusToken
    Set scanner = getObjArrayRefScanner(token)
    ' 2. 执行测试方法
    Set elem = analyzer.parseColExpr(scanner, Nothing)
    ' 3. 检查结果
    Debug.Assert elem Is token
    Debug.Assert scanner.hasNext = False

    ' ==============================
    ' 16. 扫描器内: [+] + STR_NAME
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getPlusToken, getStrNameToken("x1"))
    ' 2. 执行测试方法
    Set elem = analyzer.parseColExpr(scanner, Nothing)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterContainerCol
    Debug.Assert scanner.hasNext = False
    Debug.Assert elem.toString(config) = "+ x1"

    ' ==============================
    ' 17. 扫描器内: [-] + STR_NAME
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getMinusToken, getStrNameToken("x1"))
    ' 2. 执行测试方法
    Set elem = analyzer.parseColExpr(scanner, Nothing)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterContainerCol
    Debug.Assert scanner.hasNext = False
    Debug.Assert elem.toString(config) = "- x1"

    ' ==============================
    ' 18. 扫描器内: cast + ( + STR_NAME + AS + STR_NAME + ) + )
    '    左括号栈: 一个左括号
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getCastToken, _
        getLeftParenthesisToken, _
        getStrNameToken("x1"), getAsToken, getStrNameToken("int"), _
        getRightParenthesisToken, getRightParenthesisToken)
    Set stack = New ObjStack
    stack.push getLeftParenthesisToken

    ' 2. 执行测试方法
    Set elem = analyzer.parseColExpr(scanner, stack)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterFuncCol
    Debug.Assert scanner.hasNext = False
    Debug.Assert elem.toString(config) = "(cast(x1 as int))"

    ' ==============================
    ' 19. 扫描器内: select + STR_NAME
    '              from + STR_NAME
    '     解析一个 select 开头的子查询
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getSelectToken, getStrNameToken("x1"), _
        getFromToken, getStrNameToken("x2"))
    ' 2. 执行测试方法
    Set elem = analyzer.parseColExpr(scanner, Nothing)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterContainerEnv

    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 4
    Debug.Assert strArr.Item(0) = "select"
    Debug.Assert strArr.Item(1) = "    x1"
    Debug.Assert strArr.Item(2) = "from"
    Debug.Assert strArr.Item(3) = "    x2"

    ' ==============================
    ' 20. 扫描器内: select + STR_NAME
    '              from + STR_NAME + )
    '     解析一个 select 开头的子查询 + 吸收边界括号(一个左括号)
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getSelectToken, getStrNameToken("x1"), _
        getFromToken, getStrNameToken("x2"), _
        getRightParenthesisToken)
    ' 2. 创建左括号栈
    Set stack = New ObjStack
    stack.push getLeftParenthesisToken
    ' 3. 执行测试方法
    Set elem = analyzer.parseColExpr(scanner, stack)
    ' 4. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterContainerEnv

    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 6
    Debug.Assert strArr.Item(0) = "("
    Debug.Assert strArr.Item(1) = "    select"
    Debug.Assert strArr.Item(2) = "        x1"
    Debug.Assert strArr.Item(3) = "    from"
    Debug.Assert strArr.Item(4) = "        x2"
    Debug.Assert strArr.Item(5) = ")"

    ' ==============================
    ' 21. 扫描器内: with + RECURSIVE
    '             + STR_FUN
    '             + ( + STR_NAME + [,] + STR_NAME + )
    '             + As
    '             + (
    '                  + select + STR_NAME
    '                  + from + STR_NAME
    '             + )
    '             + )
    '    没有左括号栈 + 测试开头 with + 吸收 RECURSIVE + 一个表定义
    '     解析一个 with 开头的子查询 + 吸收边界括号(一个左括号)
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getWithToken, getRecursiveToken, _
        createFuncNameToken("t1"), _
        _
        getLeftParenthesisToken, _
        getStrNameToken("x1"), _
        getCommaToken, getStrNameToken("x2"), _
        getRightParenthesisToken, _
        _
        getAsToken, _
        _
        getLeftParenthesisToken, _
        getSelectToken, getStrNameToken("a1"), _
        getFromToken, getStrNameToken("table1"), _
        getRightParenthesisToken, _
        getRightParenthesisToken)
    ' 2. 创建左括号栈
    Set stack = New ObjStack
    stack.push getLeftParenthesisToken
    ' 3. 执行测试方法
    Set elem = analyzer.parseColExpr(scanner, stack)
    ' 4. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterWithEnv
    Debug.Assert scanner.hasNext = False
    Debug.Assert stack.Count = 0

    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 8
    Debug.Assert strArr.Item(0) = "("
    Debug.Assert strArr.Item(1) = "    with recursive t1 (x1, x2) as ("
    Debug.Assert strArr.Item(2) = "        select"
    Debug.Assert strArr.Item(3) = "            a1"
    Debug.Assert strArr.Item(4) = "        from"
    Debug.Assert strArr.Item(5) = "            table1"
    Debug.Assert strArr.Item(6) = "    )"
    Debug.Assert strArr.Item(7) = ")"
End Sub
